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