1 //! Generate Wasm modules that contain a single instruction.
2 
3 use arbitrary::{Arbitrary, Unstructured};
4 use wasm_encoder::{
5     CodeSection, ExportKind, ExportSection, Function, FunctionSection, Instruction, Module,
6     TypeSection, ValType,
7 };
8 
9 /// The name of the function generated by this module.
10 const FUNCTION_NAME: &'static str = "test";
11 
12 /// Configure a single instruction module.
13 ///
14 /// By explicitly defining the parameter and result types (versus generating the
15 /// module directly), we can more easily generate values of the right type.
16 #[derive(Clone, Debug)]
17 pub struct SingleInstModule<'a> {
18     instruction: Instruction<'a>,
19     parameters: &'a [ValType],
20     results: &'a [ValType],
21 }
22 
23 impl<'a> SingleInstModule<'a> {
24     /// Generate a binary Wasm module with a single exported function, `test`,
25     /// that executes the single instruction.
26     pub fn encode(&self) -> Vec<u8> {
27         let mut module = Module::new();
28 
29         // Encode the type section.
30         let mut types = TypeSection::new();
31         types.function(
32             self.parameters.iter().cloned(),
33             self.results.iter().cloned(),
34         );
35         module.section(&types);
36 
37         // Encode the function section.
38         let mut functions = FunctionSection::new();
39         let type_index = 0;
40         functions.function(type_index);
41         module.section(&functions);
42 
43         // Encode the export section.
44         let mut exports = ExportSection::new();
45         exports.export(FUNCTION_NAME, ExportKind::Func, 0);
46         module.section(&exports);
47 
48         // Encode the code section.
49         let mut codes = CodeSection::new();
50         let locals = vec![];
51         let mut f = Function::new(locals);
52         for (index, _) in self.parameters.iter().enumerate() {
53             f.instruction(&Instruction::LocalGet(index as u32));
54         }
55         f.instruction(&self.instruction);
56         f.instruction(&Instruction::End);
57         codes.function(&f);
58         module.section(&codes);
59 
60         // Extract the encoded Wasm bytes for this module.
61         module.finish()
62     }
63 }
64 
65 impl<'a> Arbitrary<'a> for &SingleInstModule<'_> {
66     fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
67         u.choose(&INSTRUCTIONS)
68     }
69 }
70 
71 // MACROS
72 //
73 // These macros make it a bit easier to define the instructions available for
74 // generation. The idea is that, with these macros, we can define the list of
75 // instructions compactly and allow for easier changes to the Rust code (e.g.,
76 // `SingleInstModule`).
77 
78 macro_rules! valtype {
79     (i32) => {
80         ValType::I32
81     };
82     (i64) => {
83         ValType::I64
84     };
85     (f32) => {
86         ValType::F32
87     };
88     (f64) => {
89         ValType::F64
90     };
91 }
92 
93 macro_rules! binary {
94     ($inst:ident, $rust_ty:tt) => {
95         binary! { $inst, valtype!($rust_ty), valtype!($rust_ty) }
96     };
97     ($inst:ident, $arguments_ty:expr,  $result_ty:expr) => {
98         SingleInstModule {
99             instruction: Instruction::$inst,
100             parameters: &[$arguments_ty, $arguments_ty],
101             results: &[$result_ty],
102         }
103     };
104 }
105 
106 macro_rules! compare {
107     ($inst:ident, $rust_ty:tt) => {
108         binary! { $inst, valtype!($rust_ty), ValType::I32 }
109     };
110 }
111 
112 macro_rules! unary {
113     ($inst:ident, $rust_ty:tt) => {
114         binary! { $inst, valtype!($rust_ty), valtype!($rust_ty) }
115     };
116     ($inst:ident, $argument_ty:expr, $result_ty:expr) => {
117         SingleInstModule {
118             instruction: Instruction::$inst,
119             parameters: &[$argument_ty],
120             results: &[$result_ty],
121         }
122     };
123 }
124 
125 macro_rules! convert {
126     ($inst:ident, $from_ty:tt -> $to_ty:tt) => {
127         unary! { $inst, valtype!($from_ty), valtype!($to_ty) }
128     };
129 }
130 
131 static INSTRUCTIONS: &[SingleInstModule] = &[
132     // Integer arithmetic.
133     // I32Const
134     // I64Const
135     // F32Const
136     // F64Const
137     unary!(I32Clz, i32),
138     unary!(I64Clz, i64),
139     unary!(I32Ctz, i32),
140     unary!(I64Ctz, i64),
141     unary!(I32Popcnt, i32),
142     unary!(I64Popcnt, i64),
143     binary!(I32Add, i32),
144     binary!(I64Add, i64),
145     binary!(I32Sub, i32),
146     binary!(I64Sub, i64),
147     binary!(I32Mul, i32),
148     binary!(I64Mul, i64),
149     binary!(I32DivS, i32),
150     binary!(I64DivS, i64),
151     binary!(I32DivU, i32),
152     binary!(I64DivU, i64),
153     binary!(I32RemS, i32),
154     binary!(I64RemS, i64),
155     binary!(I32RemU, i32),
156     binary!(I64RemU, i64),
157     // Integer bitwise.
158     binary!(I32And, i32),
159     binary!(I64And, i64),
160     binary!(I32Or, i32),
161     binary!(I64Or, i64),
162     binary!(I32Xor, i32),
163     binary!(I64Xor, i64),
164     binary!(I32Shl, i32),
165     binary!(I64Shl, i64),
166     binary!(I32ShrS, i32),
167     binary!(I64ShrS, i64),
168     binary!(I32ShrU, i32),
169     binary!(I64ShrU, i64),
170     binary!(I32Rotl, i32),
171     binary!(I64Rotl, i64),
172     binary!(I32Rotr, i32),
173     binary!(I64Rotr, i64),
174     // Integer comparison.
175     unary!(I32Eqz, i32),
176     unary!(I64Eqz, ValType::I64, ValType::I32),
177     compare!(I32Eq, i32),
178     compare!(I64Eq, i64),
179     compare!(I32Ne, i32),
180     compare!(I64Ne, i64),
181     compare!(I32LtS, i32),
182     compare!(I64LtS, i64),
183     compare!(I32LtU, i32),
184     compare!(I64LtU, i64),
185     compare!(I32GtS, i32),
186     compare!(I64GtS, i64),
187     compare!(I32GtU, i32),
188     compare!(I64GtU, i64),
189     compare!(I32LeS, i32),
190     compare!(I64LeS, i64),
191     compare!(I32LeU, i32),
192     compare!(I64LeU, i64),
193     compare!(I32GeS, i32),
194     compare!(I64GeS, i64),
195     compare!(I32GeU, i32),
196     compare!(I64GeU, i64),
197     // Floating-point arithmetic.
198     unary!(F32Abs, f32),
199     unary!(F64Abs, f64),
200     unary!(F32Sqrt, f32),
201     unary!(F64Sqrt, f64),
202     unary!(F32Ceil, f32),
203     unary!(F64Ceil, f64),
204     unary!(F32Floor, f32),
205     unary!(F64Floor, f64),
206     unary!(F32Trunc, f32),
207     unary!(F64Trunc, f64),
208     unary!(F32Nearest, f32),
209     unary!(F64Nearest, f64),
210     unary!(F32Neg, f32),
211     unary!(F64Neg, f64),
212     binary!(F32Add, f32),
213     binary!(F64Add, f64),
214     binary!(F32Sub, f32),
215     binary!(F64Sub, f64),
216     binary!(F32Mul, f32),
217     binary!(F64Mul, f64),
218     binary!(F32Div, f32),
219     binary!(F64Div, f64),
220     binary!(F32Min, f32),
221     binary!(F64Min, f64),
222     binary!(F32Max, f32),
223     binary!(F64Max, f64),
224     binary!(F32Copysign, f32),
225     binary!(F64Copysign, f64),
226     // Floating-point comparison.
227     compare!(F32Eq, f32),
228     compare!(F64Eq, f64),
229     compare!(F32Ne, f32),
230     compare!(F64Ne, f64),
231     compare!(F32Lt, f32),
232     compare!(F64Lt, f64),
233     compare!(F32Gt, f32),
234     compare!(F64Gt, f64),
235     compare!(F32Le, f32),
236     compare!(F64Le, f64),
237     compare!(F32Ge, f32),
238     compare!(F64Ge, f64),
239     // Integer conversions ("to integer").
240     unary!(I32Extend8S, i32),
241     unary!(I32Extend16S, i32),
242     unary!(I64Extend8S, i64),
243     unary!(I64Extend16S, i64),
244     convert!(I64Extend32S, i32 -> i64),
245     convert!(I32WrapI64, i64 -> i32),
246     convert!(I64ExtendI32S, i32 -> i64),
247     convert!(I64ExtendI32U, i32 -> i64),
248     convert!(I32TruncF32S, f32 -> i32),
249     convert!(I32TruncF32U, f32 -> i32),
250     convert!(I32TruncF64S, f64 -> i32),
251     convert!(I32TruncF64U, f64 -> i32),
252     convert!(I64TruncF32S, f32 -> i64),
253     convert!(I64TruncF32U, f32 -> i64),
254     convert!(I64TruncF64S, f64 -> i64),
255     convert!(I64TruncF64U, f64 -> i64),
256     convert!(I32TruncSatF32S, f32 -> i32),
257     convert!(I32TruncSatF32U, f32 -> i32),
258     convert!(I32TruncSatF64S, f64 -> i32),
259     convert!(I32TruncSatF64U, f64 -> i32),
260     convert!(I64TruncSatF32S, f32 -> i64),
261     convert!(I64TruncSatF32U, f32 -> i64),
262     convert!(I64TruncSatF64S, f64 -> i64),
263     convert!(I64TruncSatF64U, f64 -> i64),
264     convert!(I32ReinterpretF32, f32 -> i32),
265     convert!(I64ReinterpretF64, f64 -> i64),
266     // Floating-point conversions ("to float").
267     convert!(F32DemoteF64, f64 -> f32),
268     convert!(F64PromoteF32, f32 -> f64),
269     convert!(F32ConvertI32S, i32 -> f32),
270     convert!(F32ConvertI32U, i32 -> f32),
271     convert!(F32ConvertI64S, i64 -> f32),
272     convert!(F32ConvertI64U, i64 -> f32),
273     convert!(F64ConvertI32S, i32 -> f64),
274     convert!(F64ConvertI32U, i32 -> f64),
275     convert!(F64ConvertI64S, i64 -> f64),
276     convert!(F64ConvertI64U, i64 -> f64),
277     convert!(F32ReinterpretI32, i32 -> f32),
278     convert!(F64ReinterpretI64, i64 -> f64),
279 ];
280 
281 #[cfg(test)]
282 mod test {
283     use super::*;
284 
285     #[test]
286     fn sanity() {
287         let sut = SingleInstModule {
288             instruction: Instruction::I32Add,
289             parameters: &[ValType::I32, ValType::I32],
290             results: &[ValType::I32],
291         };
292         let wasm = sut.encode();
293         let wat = wasmprinter::print_bytes(wasm).unwrap();
294         assert_eq!(
295             wat,
296             r#"(module
297   (type (;0;) (func (param i32 i32) (result i32)))
298   (func (;0;) (type 0) (param i32 i32) (result i32)
299     local.get 0
300     local.get 1
301     i32.add
302   )
303   (export "test" (func 0))
304 )"#
305         )
306     }
307 
308     #[test]
309     fn instructions_encode_to_valid_modules() {
310         for inst in INSTRUCTIONS {
311             assert!(wat::parse_bytes(&inst.encode()).is_ok());
312         }
313     }
314 }
315