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