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