1 //! Generate Wasm modules that contain a single instruction. 2 3 use super::ModuleConfig; 4 use arbitrary::Unstructured; 5 use wasm_encoder::{ 6 CodeSection, ExportKind, ExportSection, Function, FunctionSection, Instruction, Module, 7 TypeSection, ValType, 8 }; 9 10 /// The name of the function generated by this module. 11 const FUNCTION_NAME: &'static str = "test"; 12 13 /// Configure a single instruction module. 14 /// 15 /// By explicitly defining the parameter and result types (versus generating the 16 /// module directly), we can more easily generate values of the right type. 17 #[derive(Clone)] 18 pub struct SingleInstModule<'a> { 19 instruction: Instruction<'a>, 20 parameters: &'a [ValType], 21 results: &'a [ValType], 22 feature: fn(&ModuleConfig) -> bool, 23 canonicalize_nan: Option<NanType>, 24 } 25 26 /// Valid types for NaN canonicalization. 27 /// 28 /// When fuzzing floating point values, a NaN result can have non-deterministic 29 /// bits in the payload. In order to compare these results, [`SingleInstModule`] 30 /// can convert any NaN values (or NaN lanes) to a canonical NaN value for any 31 /// of these types. 32 #[derive(Clone)] 33 enum NanType { 34 #[expect(dead_code, reason = "expected to be used in the future")] 35 F32, 36 #[expect(dead_code, reason = "expected to be used in the future")] 37 F64, 38 F32x4, 39 F64x2, 40 } 41 42 impl<'a> SingleInstModule<'a> { 43 /// Choose a single-instruction module that matches `config`. new(u: &mut Unstructured<'a>, config: &ModuleConfig) -> arbitrary::Result<&'a Self>44 pub fn new(u: &mut Unstructured<'a>, config: &ModuleConfig) -> arbitrary::Result<&'a Self> { 45 // Only select instructions that match the `ModuleConfig`. 46 let instructions = &INSTRUCTIONS 47 .iter() 48 .filter(|i| (i.feature)(config)) 49 .collect::<Vec<_>>(); 50 u.choose(&instructions[..]).copied() 51 } 52 53 /// Encode a binary Wasm module with a single exported function, `test`, 54 /// that executes the single instruction. to_bytes(&self) -> Vec<u8>55 pub fn to_bytes(&self) -> Vec<u8> { 56 let mut module = Module::new(); 57 58 // Encode the type section. 59 let mut types = TypeSection::new(); 60 types.ty().function( 61 self.parameters.iter().cloned(), 62 self.results.iter().cloned(), 63 ); 64 module.section(&types); 65 66 // Encode the function section. 67 let mut functions = FunctionSection::new(); 68 let type_index = 0; 69 functions.function(type_index); 70 module.section(&functions); 71 72 // Encode the export section. 73 let mut exports = ExportSection::new(); 74 exports.export(FUNCTION_NAME, ExportKind::Func, 0); 75 module.section(&exports); 76 77 // Encode the code section. 78 let mut codes = CodeSection::new(); 79 80 // Set up the single-instruction function. Note that if we have chosen 81 // to canonicalize NaNs, this function will contain more than one 82 // instruction and the function will need a scratch local. 83 let mut f = if let Some(ty) = &self.canonicalize_nan { 84 Function::new(match ty { 85 NanType::F32 => vec![(1, ValType::F32)], 86 NanType::F64 => vec![(1, ValType::F64)], 87 NanType::F32x4 | NanType::F64x2 => vec![(1, ValType::V128)], 88 }) 89 } else { 90 Function::new([]) 91 }; 92 93 // Retrieve the input values and execute the chosen instruction. 94 for (index, _) in self.parameters.iter().enumerate() { 95 f.instruction(&Instruction::LocalGet(index as u32)); 96 } 97 f.instruction(&self.instruction); 98 99 // If we have configured to canonicalize NaNs, we add a sequence that 100 // masks off the NaN payload bits to make them 0s (i.e., a canonical 101 // NaN). This sequence is adapted from wasm-smiths version; see 102 // https://github.com/bytecodealliance/wasm-tools/blob/6c127a6/crates/wasm-smith/src/core/code_builder.rs#L927. 103 if let Some(ty) = &self.canonicalize_nan { 104 // Save the previous instruction's result into the scratch local. 105 // This also leaves a value on the stack as for the `select` 106 // instruction. 107 let local = self.parameters.len() as u32; 108 f.instruction(&Instruction::LocalTee(local)); 109 110 // The other input to the `select` below--a canonical NaN. Note how 111 // the payload bits of the NaN are cleared. 112 const CANON_32BIT_NAN: u32 = 0b01111111110000000000000000000000; 113 const CANON_64BIT_NAN: u64 = 114 0b0111111111111000000000000000000000000000000000000000000000000000; 115 let mask = match ty { 116 NanType::F32 => Instruction::F32Const(f32::from_bits(CANON_32BIT_NAN).into()), 117 NanType::F64 => Instruction::F64Const(f64::from_bits(CANON_64BIT_NAN).into()), 118 NanType::F32x4 => { 119 let nan = CANON_32BIT_NAN as i128; 120 Instruction::V128Const(nan | (nan << 32) | (nan << 64) | (nan << 96)) 121 } 122 NanType::F64x2 => { 123 let nan = CANON_64BIT_NAN as i128; 124 Instruction::V128Const(nan | (nan << 64)) 125 } 126 }; 127 f.instruction(&mask); 128 129 // The `select` condition. NaNs never equal each other, so here the 130 // result value is compared against itself. 131 f.instruction(&Instruction::LocalGet(local)); 132 f.instruction(&Instruction::LocalGet(local)); 133 f.instruction(match ty { 134 NanType::F32 => &Instruction::F32Eq, 135 NanType::F64 => &Instruction::F64Eq, 136 NanType::F32x4 => &Instruction::F32x4Eq, 137 NanType::F64x2 => &Instruction::F64x2Eq, 138 }); 139 140 // Select the result. If the condition is nonzero (i.e., the float 141 // is equal to itself) it picks the original value; otherwise, if 142 // zero (i.e., the float is a NaN) it picks the canonical NaN value. 143 f.instruction(match ty { 144 NanType::F32 | NanType::F64 => &Instruction::Select, 145 NanType::F32x4 | NanType::F64x2 => &Instruction::V128Bitselect, 146 }); 147 } 148 149 // Wrap up the function and section. 150 f.instruction(&Instruction::End); 151 codes.function(&f); 152 module.section(&codes); 153 154 // Extract the encoded Wasm bytes for this module. 155 module.finish() 156 } 157 } 158 159 // MACROS 160 // 161 // These macros make it a bit easier to define the instructions available for 162 // generation. The idea is that, with these macros, we can define the list of 163 // instructions compactly and allow for easier changes to the Rust code (e.g., 164 // `SingleInstModule`). 165 166 macro_rules! valtypes { 167 (@list ($($ty:tt),*)) => {&[$(valtypes!(@one $ty)),*]}; 168 (@list $ty:tt) => {&[valtypes!(@one $ty)]}; 169 (@one i32) => { 170 ValType::I32 171 }; 172 (@one i64) => { 173 ValType::I64 174 }; 175 (@one f32) => { 176 ValType::F32 177 }; 178 (@one f64) => { 179 ValType::F64 180 }; 181 (@one v128) => { 182 ValType::V128 183 }; 184 } 185 186 macro_rules! inst { 187 ($inst:ident, $arguments:tt -> $results:tt) => { 188 inst! { $inst, $arguments -> $results, |_| true } 189 }; 190 ($inst:ident, $arguments:tt -> $results:tt, $feature:expr) => { 191 inst! { $inst, $arguments -> $results, $feature, None } 192 }; 193 ($inst:ident, $arguments:tt -> $results:tt, $feature:expr, $nan:expr) => { 194 SingleInstModule { 195 instruction: Instruction::$inst, 196 parameters: valtypes!(@list $arguments), 197 results: valtypes!(@list $results), 198 feature: $feature, 199 canonicalize_nan: $nan, 200 } 201 }; 202 } 203 204 // INSTRUCTIONS 205 // 206 // This list of WebAssembly instructions attempts to roughly follow the 207 // structure of the W3C specification: 208 // https://webassembly.github.io/spec/core/appendix/index-instructions.html#index-instr. 209 // Certain kinds of instructions (e.g., memory access) are skipped for now. 210 static INSTRUCTIONS: &[SingleInstModule] = &[ 211 // Integer arithmetic. 212 // I32Const 213 // I64Const 214 // F32Const 215 // F64Const 216 inst!(I32Clz, (i32) -> i32), 217 inst!(I64Clz, (i64) -> i64), 218 inst!(I32Ctz, (i32) -> i32), 219 inst!(I64Ctz, (i64) -> i64), 220 inst!(I32Popcnt, (i32) -> i32), 221 inst!(I64Popcnt, (i64) -> i64), 222 inst!(I32Add, (i32, i32) -> i32), 223 inst!(I64Add, (i64, i64) -> i64), 224 inst!(I32Sub, (i32, i32) -> i32), 225 inst!(I64Sub, (i64, i64) -> i64), 226 inst!(I32Mul, (i32, i32) -> i32), 227 inst!(I64Mul, (i64, i64) -> i64), 228 inst!(I32DivS, (i32, i32) -> i32), 229 inst!(I64DivS, (i64, i64) -> i64), 230 inst!(I32DivU, (i32, i32) -> i32), 231 inst!(I64DivU, (i64, i64) -> i64), 232 inst!(I32RemS, (i32, i32) -> i32), 233 inst!(I64RemS, (i64, i64) -> i64), 234 inst!(I32RemU, (i32, i32) -> i32), 235 inst!(I64RemU, (i64, i64) -> i64), 236 // Integer bitwise. 237 inst!(I32And, (i32, i32) -> i32), 238 inst!(I64And, (i64, i64) -> i64), 239 inst!(I32Or, (i32, i32) -> i32), 240 inst!(I64Or, (i64, i64) -> i64), 241 inst!(I32Xor, (i32, i32) -> i32), 242 inst!(I64Xor, (i64, i64) -> i64), 243 inst!(I32Shl, (i32, i32) -> i32), 244 inst!(I64Shl, (i64, i64) -> i64), 245 inst!(I32ShrS, (i32, i32) -> i32), 246 inst!(I64ShrS, (i64, i64) -> i64), 247 inst!(I32ShrU, (i32, i32) -> i32), 248 inst!(I64ShrU, (i64, i64) -> i64), 249 inst!(I32Rotl, (i32, i32) -> i32), 250 inst!(I64Rotl, (i64, i64) -> i64), 251 inst!(I32Rotr, (i32, i32) -> i32), 252 inst!(I64Rotr, (i64, i64) -> i64), 253 // Integer comparison. 254 inst!(I32Eqz, (i32) -> i32), 255 inst!(I64Eqz, (i64) -> i32), 256 inst!(I32Eq, (i32, i32) -> i32), 257 inst!(I64Eq, (i64, i64) -> i32), 258 inst!(I32Ne, (i32, i32) -> i32), 259 inst!(I64Ne, (i64, i64) -> i32), 260 inst!(I32LtS, (i32, i32) -> i32), 261 inst!(I64LtS, (i64, i64) -> i32), 262 inst!(I32LtU, (i32, i32) -> i32), 263 inst!(I64LtU, (i64, i64) -> i32), 264 inst!(I32GtS, (i32, i32) -> i32), 265 inst!(I64GtS, (i64, i64) -> i32), 266 inst!(I32GtU, (i32, i32) -> i32), 267 inst!(I64GtU, (i64, i64) -> i32), 268 inst!(I32LeS, (i32, i32) -> i32), 269 inst!(I64LeS, (i64, i64) -> i32), 270 inst!(I32LeU, (i32, i32) -> i32), 271 inst!(I64LeU, (i64, i64) -> i32), 272 inst!(I32GeS, (i32, i32) -> i32), 273 inst!(I64GeS, (i64, i64) -> i32), 274 inst!(I32GeU, (i32, i32) -> i32), 275 inst!(I64GeU, (i64, i64) -> i32), 276 // Floating-point arithmetic. 277 inst!(F32Abs, (f32) -> f32), 278 inst!(F64Abs, (f64) -> f64), 279 inst!(F32Sqrt, (f32) -> f32), 280 inst!(F64Sqrt, (f64) -> f64), 281 inst!(F32Ceil, (f32) -> f32), 282 inst!(F64Ceil, (f64) -> f64), 283 inst!(F32Floor, (f32) -> f32), 284 inst!(F64Floor, (f64) -> f64), 285 inst!(F32Trunc, (f32) -> f32), 286 inst!(F64Trunc, (f64) -> f64), 287 inst!(F32Nearest, (f32) -> f32), 288 inst!(F64Nearest, (f64) -> f64), 289 inst!(F32Neg, (f32) -> f32), 290 inst!(F64Neg, (f64) -> f64), 291 inst!(F32Add, (f32, f32) -> f32), 292 inst!(F64Add, (f64, f64) -> f64), 293 inst!(F32Sub, (f32, f32) -> f32), 294 inst!(F64Sub, (f64, f64) -> f64), 295 inst!(F32Mul, (f32, f32) -> f32), 296 inst!(F64Mul, (f64, f64) -> f64), 297 inst!(F32Div, (f32, f32) -> f32), 298 inst!(F64Div, (f64, f64) -> f64), 299 inst!(F32Min, (f32, f32) -> f32), 300 inst!(F64Min, (f64, f64) -> f64), 301 inst!(F32Max, (f32, f32) -> f32), 302 inst!(F64Max, (f64, f64) -> f64), 303 inst!(F32Copysign, (f32, f32) -> f32), 304 inst!(F64Copysign, (f64, f64) -> f64), 305 // Floating-point comparison. 306 inst!(F32Eq, (f32, f32) -> i32), 307 inst!(F64Eq, (f64, f64) -> i32), 308 inst!(F32Ne, (f32, f32) -> i32), 309 inst!(F64Ne, (f64, f64) -> i32), 310 inst!(F32Lt, (f32, f32) -> i32), 311 inst!(F64Lt, (f64, f64) -> i32), 312 inst!(F32Gt, (f32, f32) -> i32), 313 inst!(F64Gt, (f64, f64) -> i32), 314 inst!(F32Le, (f32, f32) -> i32), 315 inst!(F64Le, (f64, f64) -> i32), 316 inst!(F32Ge, (f32, f32) -> i32), 317 inst!(F64Ge, (f64, f64) -> i32), 318 // Integer conversions ("to integer"). 319 inst!(I32Extend8S, (i32) -> i32, |c| c.config.sign_extension_ops_enabled), 320 inst!(I32Extend16S, (i32) -> i32, |c| c.config.sign_extension_ops_enabled), 321 inst!(I64Extend8S, (i64) -> i64, |c| c.config.sign_extension_ops_enabled), 322 inst!(I64Extend16S, (i64) -> i64, |c| c.config.sign_extension_ops_enabled), 323 inst!(I64Extend32S, (i64) -> i64, |c| c.config.sign_extension_ops_enabled), 324 inst!(I32WrapI64, (i64) -> i32), 325 inst!(I64ExtendI32S, (i32) -> i64), 326 inst!(I64ExtendI32U, (i32) -> i64), 327 inst!(I32TruncF32S, (f32) -> i32), 328 inst!(I32TruncF32U, (f32) -> i32), 329 inst!(I32TruncF64S, (f64) -> i32), 330 inst!(I32TruncF64U, (f64) -> i32), 331 inst!(I64TruncF32S, (f32) -> i64), 332 inst!(I64TruncF32U, (f32) -> i64), 333 inst!(I64TruncF64S, (f64) -> i64), 334 inst!(I64TruncF64U, (f64) -> i64), 335 inst!(I32TruncSatF32S, (f32) -> i32, |c| c.config.saturating_float_to_int_enabled), 336 inst!(I32TruncSatF32U, (f32) -> i32, |c| c.config.saturating_float_to_int_enabled), 337 inst!(I32TruncSatF64S, (f64) -> i32, |c| c.config.saturating_float_to_int_enabled), 338 inst!(I32TruncSatF64U, (f64) -> i32, |c| c.config.saturating_float_to_int_enabled), 339 inst!(I64TruncSatF32S, (f32) -> i64, |c| c.config.saturating_float_to_int_enabled), 340 inst!(I64TruncSatF32U, (f32) -> i64, |c| c.config.saturating_float_to_int_enabled), 341 inst!(I64TruncSatF64S, (f64) -> i64, |c| c.config.saturating_float_to_int_enabled), 342 inst!(I64TruncSatF64U, (f64) -> i64, |c| c.config.saturating_float_to_int_enabled), 343 inst!(I32ReinterpretF32, (f32) -> i32), 344 inst!(I64ReinterpretF64, (f64) -> i64), 345 // Floating-point conversions ("to float"). 346 inst!(F32DemoteF64, (f64) -> f32), 347 inst!(F64PromoteF32, (f32) -> f64), 348 inst!(F32ConvertI32S, (i32) -> f32), 349 inst!(F32ConvertI32U, (i32) -> f32), 350 inst!(F32ConvertI64S, (i64) -> f32), 351 inst!(F32ConvertI64U, (i64) -> f32), 352 inst!(F64ConvertI32S, (i32) -> f64), 353 inst!(F64ConvertI32U, (i32) -> f64), 354 inst!(F64ConvertI64S, (i64) -> f64), 355 inst!(F64ConvertI64U, (i64) -> f64), 356 inst!(F32ReinterpretI32, (i32) -> f32), 357 inst!(F64ReinterpretI64, (i64) -> f64), 358 // SIMD instructions. 359 // V128Const 360 // I8x16Shuffle 361 inst!(I8x16Swizzle, (v128, v128) -> v128, |c| c.config.simd_enabled), 362 inst!(I8x16Splat, (i32) -> v128, |c| c.config.simd_enabled), 363 inst!(I16x8Splat, (i32) -> v128, |c| c.config.simd_enabled), 364 inst!(I32x4Splat, (i32) -> v128, |c| c.config.simd_enabled), 365 inst!(I64x2Splat, (i64) -> v128, |c| c.config.simd_enabled), 366 inst!(F32x4Splat, (f32) -> v128, |c| c.config.simd_enabled), 367 inst!(F64x2Splat, (f64) -> v128, |c| c.config.simd_enabled), 368 // I8x16ExtractLaneS 369 // I8x16ExtractLaneU 370 // I8x16ReplaceLane 371 // I16x8ExtractLaneS 372 // I16x8ExtractLaneU 373 // I16x8ReplaceLane 374 // I32x4ExtractLane 375 // I32x4ReplaceLane 376 // I64x2ExtractLane 377 // I64x2ReplaceLane 378 // F32x4ExtractLane 379 // F32x4ReplaceLane 380 // F64x2ExtractLane 381 // F64x2ReplaceLane 382 inst!(I8x16Eq, (v128, v128) -> v128, |c| c.config.simd_enabled), 383 inst!(I8x16Ne, (v128, v128) -> v128, |c| c.config.simd_enabled), 384 inst!(I8x16LtS, (v128, v128) -> v128, |c| c.config.simd_enabled), 385 inst!(I8x16LtU, (v128, v128) -> v128, |c| c.config.simd_enabled), 386 inst!(I8x16GtS, (v128, v128) -> v128, |c| c.config.simd_enabled), 387 inst!(I8x16GtU, (v128, v128) -> v128, |c| c.config.simd_enabled), 388 inst!(I8x16LeS, (v128, v128) -> v128, |c| c.config.simd_enabled), 389 inst!(I8x16LeU, (v128, v128) -> v128, |c| c.config.simd_enabled), 390 inst!(I8x16GeS, (v128, v128) -> v128, |c| c.config.simd_enabled), 391 inst!(I8x16GeU, (v128, v128) -> v128, |c| c.config.simd_enabled), 392 inst!(I16x8Eq, (v128, v128) -> v128, |c| c.config.simd_enabled), 393 inst!(I16x8Ne, (v128, v128) -> v128, |c| c.config.simd_enabled), 394 inst!(I16x8LtS, (v128, v128) -> v128, |c| c.config.simd_enabled), 395 inst!(I16x8LtU, (v128, v128) -> v128, |c| c.config.simd_enabled), 396 inst!(I16x8GtS, (v128, v128) -> v128, |c| c.config.simd_enabled), 397 inst!(I16x8GtU, (v128, v128) -> v128, |c| c.config.simd_enabled), 398 inst!(I16x8LeS, (v128, v128) -> v128, |c| c.config.simd_enabled), 399 inst!(I16x8LeU, (v128, v128) -> v128, |c| c.config.simd_enabled), 400 inst!(I16x8GeS, (v128, v128) -> v128, |c| c.config.simd_enabled), 401 inst!(I16x8GeU, (v128, v128) -> v128, |c| c.config.simd_enabled), 402 inst!(I32x4Eq, (v128, v128) -> v128, |c| c.config.simd_enabled), 403 inst!(I32x4Ne, (v128, v128) -> v128, |c| c.config.simd_enabled), 404 inst!(I32x4LtS, (v128, v128) -> v128, |c| c.config.simd_enabled), 405 inst!(I32x4LtU, (v128, v128) -> v128, |c| c.config.simd_enabled), 406 inst!(I32x4GtS, (v128, v128) -> v128, |c| c.config.simd_enabled), 407 inst!(I32x4GtU, (v128, v128) -> v128, |c| c.config.simd_enabled), 408 inst!(I32x4LeS, (v128, v128) -> v128, |c| c.config.simd_enabled), 409 inst!(I32x4LeU, (v128, v128) -> v128, |c| c.config.simd_enabled), 410 inst!(I32x4GeS, (v128, v128) -> v128, |c| c.config.simd_enabled), 411 inst!(I32x4GeU, (v128, v128) -> v128, |c| c.config.simd_enabled), 412 inst!(I64x2Eq, (v128, v128) -> v128, |c| c.config.simd_enabled), 413 inst!(I64x2Ne, (v128, v128) -> v128, |c| c.config.simd_enabled), 414 inst!(I64x2LtS, (v128, v128) -> v128, |c| c.config.simd_enabled), 415 inst!(I64x2GtS, (v128, v128) -> v128, |c| c.config.simd_enabled), 416 inst!(I64x2LeS, (v128, v128) -> v128, |c| c.config.simd_enabled), 417 inst!(I64x2GeS, (v128, v128) -> v128, |c| c.config.simd_enabled), 418 inst!(F32x4Eq, (v128, v128) -> v128, |c| c.config.simd_enabled), 419 inst!(F32x4Ne, (v128, v128) -> v128, |c| c.config.simd_enabled), 420 inst!(F32x4Lt, (v128, v128) -> v128, |c| c.config.simd_enabled), 421 inst!(F32x4Gt, (v128, v128) -> v128, |c| c.config.simd_enabled), 422 inst!(F32x4Le, (v128, v128) -> v128, |c| c.config.simd_enabled), 423 inst!(F32x4Ge, (v128, v128) -> v128, |c| c.config.simd_enabled), 424 inst!(F64x2Eq, (v128, v128) -> v128, |c| c.config.simd_enabled), 425 inst!(F64x2Ne, (v128, v128) -> v128, |c| c.config.simd_enabled), 426 inst!(F64x2Lt, (v128, v128) -> v128, |c| c.config.simd_enabled), 427 inst!(F64x2Gt, (v128, v128) -> v128, |c| c.config.simd_enabled), 428 inst!(F64x2Le, (v128, v128) -> v128, |c| c.config.simd_enabled), 429 inst!(F64x2Ge, (v128, v128) -> v128, |c| c.config.simd_enabled), 430 inst!(V128Not, (v128) -> v128, |c| c.config.simd_enabled), 431 inst!(V128And, (v128, v128) -> v128, |c| c.config.simd_enabled), 432 inst!(V128AndNot, (v128, v128) -> v128, |c| c.config.simd_enabled), 433 inst!(V128Or, (v128, v128) -> v128, |c| c.config.simd_enabled), 434 inst!(V128Xor, (v128, v128) -> v128, |c| c.config.simd_enabled), 435 inst!(V128Bitselect, (v128, v128, v128) -> v128, |c| c.config.simd_enabled), 436 inst!(V128AnyTrue, (v128) -> i32, |c| c.config.simd_enabled), 437 inst!(I8x16Abs, (v128) -> v128, |c| c.config.simd_enabled), 438 inst!(I8x16Neg, (v128) -> v128, |c| c.config.simd_enabled), 439 inst!(I8x16Popcnt, (v128) -> v128, |c| c.config.simd_enabled), 440 inst!(I8x16AllTrue, (v128) -> i32, |c| c.config.simd_enabled), 441 inst!(I8x16Bitmask, (v128) -> i32, |c| c.config.simd_enabled), 442 inst!(I8x16NarrowI16x8S, (v128, v128) -> v128, |c| c.config.simd_enabled), 443 inst!(I8x16NarrowI16x8U, (v128, v128) -> v128, |c| c.config.simd_enabled), 444 inst!(I8x16Shl, (v128, i32) -> v128, |c| c.config.simd_enabled), 445 inst!(I8x16ShrS, (v128, i32) -> v128, |c| c.config.simd_enabled), 446 inst!(I8x16ShrU, (v128, i32) -> v128, |c| c.config.simd_enabled), 447 inst!(I8x16Add, (v128, v128) -> v128, |c| c.config.simd_enabled), 448 inst!(I8x16AddSatS, (v128, v128) -> v128, |c| c.config.simd_enabled), 449 inst!(I8x16AddSatU, (v128, v128) -> v128, |c| c.config.simd_enabled), 450 inst!(I8x16Sub, (v128, v128) -> v128, |c| c.config.simd_enabled), 451 inst!(I8x16SubSatS, (v128, v128) -> v128, |c| c.config.simd_enabled), 452 inst!(I8x16SubSatU, (v128, v128) -> v128, |c| c.config.simd_enabled), 453 inst!(I8x16MinS, (v128, v128) -> v128, |c| c.config.simd_enabled), 454 inst!(I8x16MinU, (v128, v128) -> v128, |c| c.config.simd_enabled), 455 inst!(I8x16MaxS, (v128, v128) -> v128, |c| c.config.simd_enabled), 456 inst!(I8x16MaxU, (v128, v128) -> v128, |c| c.config.simd_enabled), 457 inst!(I8x16AvgrU, (v128, v128) -> v128, |c| c.config.simd_enabled), 458 inst!(I16x8ExtAddPairwiseI8x16S, (v128) -> v128, |c| c.config.simd_enabled), 459 inst!(I16x8ExtAddPairwiseI8x16U, (v128) -> v128, |c| c.config.simd_enabled), 460 inst!(I16x8Abs, (v128) -> v128, |c| c.config.simd_enabled), 461 inst!(I16x8Neg, (v128) -> v128, |c| c.config.simd_enabled), 462 inst!(I16x8Q15MulrSatS, (v128, v128) -> v128, |c| c.config.simd_enabled), 463 inst!(I16x8AllTrue, (v128) -> i32, |c| c.config.simd_enabled), 464 inst!(I16x8Bitmask, (v128) -> i32, |c| c.config.simd_enabled), 465 inst!(I16x8NarrowI32x4S, (v128, v128) -> v128, |c| c.config.simd_enabled), 466 inst!(I16x8NarrowI32x4U, (v128, v128) -> v128, |c| c.config.simd_enabled), 467 inst!(I16x8ExtendLowI8x16S, (v128) -> v128, |c| c.config.simd_enabled), 468 inst!(I16x8ExtendHighI8x16S, (v128) -> v128, |c| c.config.simd_enabled), 469 inst!(I16x8ExtendLowI8x16U, (v128) -> v128, |c| c.config.simd_enabled), 470 inst!(I16x8ExtendHighI8x16U, (v128) -> v128, |c| c.config.simd_enabled), 471 inst!(I16x8Shl, (v128, i32) -> v128, |c| c.config.simd_enabled), 472 inst!(I16x8ShrS, (v128, i32) -> v128, |c| c.config.simd_enabled), 473 inst!(I16x8ShrU, (v128, i32) -> v128, |c| c.config.simd_enabled), 474 inst!(I16x8Add, (v128, v128) -> v128, |c| c.config.simd_enabled), 475 inst!(I16x8AddSatS, (v128, v128) -> v128, |c| c.config.simd_enabled), 476 inst!(I16x8AddSatU, (v128, v128) -> v128, |c| c.config.simd_enabled), 477 inst!(I16x8Sub, (v128, v128) -> v128, |c| c.config.simd_enabled), 478 inst!(I16x8SubSatS, (v128, v128) -> v128, |c| c.config.simd_enabled), 479 inst!(I16x8SubSatU, (v128, v128) -> v128, |c| c.config.simd_enabled), 480 inst!(I16x8Mul, (v128, v128) -> v128, |c| c.config.simd_enabled), 481 inst!(I16x8MinS, (v128, v128) -> v128, |c| c.config.simd_enabled), 482 inst!(I16x8MinU, (v128, v128) -> v128, |c| c.config.simd_enabled), 483 inst!(I16x8MaxS, (v128, v128) -> v128, |c| c.config.simd_enabled), 484 inst!(I16x8MaxU, (v128, v128) -> v128, |c| c.config.simd_enabled), 485 inst!(I16x8AvgrU, (v128, v128) -> v128, |c| c.config.simd_enabled), 486 inst!(I16x8ExtMulLowI8x16S, (v128, v128) -> v128, |c| c.config.simd_enabled), 487 inst!(I16x8ExtMulHighI8x16S, (v128, v128) -> v128, |c| c.config.simd_enabled), 488 inst!(I16x8ExtMulLowI8x16U, (v128, v128) -> v128, |c| c.config.simd_enabled), 489 inst!(I16x8ExtMulHighI8x16U, (v128, v128) -> v128, |c| c.config.simd_enabled), 490 inst!(I32x4ExtAddPairwiseI16x8S, (v128) -> v128, |c| c.config.simd_enabled), 491 inst!(I32x4ExtAddPairwiseI16x8U, (v128) -> v128, |c| c.config.simd_enabled), 492 inst!(I32x4Abs, (v128) -> v128, |c| c.config.simd_enabled), 493 inst!(I32x4Neg, (v128) -> v128, |c| c.config.simd_enabled), 494 inst!(I32x4AllTrue, (v128) -> i32, |c| c.config.simd_enabled), 495 inst!(I32x4Bitmask, (v128) -> i32, |c| c.config.simd_enabled), 496 inst!(I32x4ExtendLowI16x8S, (v128) -> v128, |c| c.config.simd_enabled), 497 inst!(I32x4ExtendHighI16x8S, (v128) -> v128, |c| c.config.simd_enabled), 498 inst!(I32x4ExtendLowI16x8U, (v128) -> v128, |c| c.config.simd_enabled), 499 inst!(I32x4ExtendHighI16x8U, (v128) -> v128, |c| c.config.simd_enabled), 500 inst!(I32x4Shl, (v128, i32) -> v128, |c| c.config.simd_enabled), 501 inst!(I32x4ShrS, (v128, i32) -> v128, |c| c.config.simd_enabled), 502 inst!(I32x4ShrU, (v128, i32) -> v128, |c| c.config.simd_enabled), 503 inst!(I32x4Add, (v128, v128) -> v128, |c| c.config.simd_enabled), 504 inst!(I32x4Sub, (v128, v128) -> v128, |c| c.config.simd_enabled), 505 inst!(I32x4Mul, (v128, v128) -> v128, |c| c.config.simd_enabled), 506 inst!(I32x4MinS, (v128, v128) -> v128, |c| c.config.simd_enabled), 507 inst!(I32x4MinU, (v128, v128) -> v128, |c| c.config.simd_enabled), 508 inst!(I32x4MaxS, (v128, v128) -> v128, |c| c.config.simd_enabled), 509 inst!(I32x4MaxU, (v128, v128) -> v128, |c| c.config.simd_enabled), 510 inst!(I32x4DotI16x8S, (v128, v128) -> v128, |c| c.config.simd_enabled), 511 inst!(I32x4ExtMulLowI16x8S, (v128, v128) -> v128, |c| c.config.simd_enabled), 512 inst!(I32x4ExtMulHighI16x8S, (v128, v128) -> v128, |c| c.config.simd_enabled), 513 inst!(I32x4ExtMulLowI16x8U, (v128, v128) -> v128, |c| c.config.simd_enabled), 514 inst!(I32x4ExtMulHighI16x8U, (v128, v128) -> v128, |c| c.config.simd_enabled), 515 inst!(I64x2Abs, (v128) -> v128, |c| c.config.simd_enabled), 516 inst!(I64x2Neg, (v128) -> v128, |c| c.config.simd_enabled), 517 inst!(I64x2AllTrue, (v128) -> i32, |c| c.config.simd_enabled), 518 inst!(I64x2Bitmask, (v128) -> i32, |c| c.config.simd_enabled), 519 inst!(I64x2ExtendLowI32x4S, (v128) -> v128, |c| c.config.simd_enabled), 520 inst!(I64x2ExtendHighI32x4S, (v128) -> v128, |c| c.config.simd_enabled), 521 inst!(I64x2ExtendLowI32x4U, (v128) -> v128, |c| c.config.simd_enabled), 522 inst!(I64x2ExtendHighI32x4U, (v128) -> v128, |c| c.config.simd_enabled), 523 inst!(I64x2Shl, (v128, i32) -> v128, |c| c.config.simd_enabled), 524 inst!(I64x2ShrS, (v128, i32) -> v128, |c| c.config.simd_enabled), 525 inst!(I64x2ShrU, (v128, i32) -> v128, |c| c.config.simd_enabled), 526 inst!(I64x2Add, (v128, v128) -> v128, |c| c.config.simd_enabled), 527 inst!(I64x2Sub, (v128, v128) -> v128, |c| c.config.simd_enabled), 528 inst!(I64x2Mul, (v128, v128) -> v128, |c| c.config.simd_enabled), 529 inst!(I64x2ExtMulLowI32x4S, (v128, v128) -> v128, |c| c.config.simd_enabled), 530 inst!(I64x2ExtMulHighI32x4S, (v128, v128) -> v128, |c| c.config.simd_enabled), 531 inst!(I64x2ExtMulLowI32x4U, (v128, v128) -> v128, |c| c.config.simd_enabled), 532 inst!(I64x2ExtMulHighI32x4U, (v128, v128) -> v128, |c| c.config.simd_enabled), 533 inst!(F32x4Ceil, (v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F32x4)), 534 inst!(F32x4Floor, (v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F32x4)), 535 inst!(F32x4Trunc, (v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F32x4)), 536 inst!(F32x4Nearest, (v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F32x4)), 537 inst!(F32x4Abs, (v128) -> v128, |c| c.config.simd_enabled), 538 inst!(F32x4Neg, (v128) -> v128, |c| c.config.simd_enabled), 539 inst!(F32x4Sqrt, (v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F32x4)), 540 inst!(F32x4Add, (v128, v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F32x4)), 541 inst!(F32x4Sub, (v128, v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F32x4)), 542 inst!(F32x4Mul, (v128, v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F32x4)), 543 inst!(F32x4Div, (v128, v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F32x4)), 544 inst!(F32x4Min, (v128, v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F32x4)), 545 inst!(F32x4Max, (v128, v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F32x4)), 546 inst!(F32x4PMin, (v128, v128) -> v128, |c| c.config.simd_enabled), 547 inst!(F32x4PMax, (v128, v128) -> v128, |c| c.config.simd_enabled), 548 inst!(F64x2Ceil, (v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F64x2)), 549 inst!(F64x2Floor, (v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F64x2)), 550 inst!(F64x2Trunc, (v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F64x2)), 551 inst!(F64x2Nearest, (v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F64x2)), 552 inst!(F64x2Abs, (v128) -> v128, |c| c.config.simd_enabled), 553 inst!(F64x2Neg, (v128) -> v128, |c| c.config.simd_enabled), 554 inst!(F64x2Sqrt, (v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F64x2)), 555 inst!(F64x2Add, (v128, v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F64x2)), 556 inst!(F64x2Sub, (v128, v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F64x2)), 557 inst!(F64x2Mul, (v128, v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F64x2)), 558 inst!(F64x2Div, (v128, v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F64x2)), 559 inst!(F64x2Min, (v128, v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F64x2)), 560 inst!(F64x2Max, (v128, v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F64x2)), 561 inst!(F64x2PMin, (v128, v128) -> v128, |c| c.config.simd_enabled), 562 inst!(F64x2PMax, (v128, v128) -> v128, |c| c.config.simd_enabled), 563 inst!(I32x4TruncSatF32x4S, (v128) -> v128, |c| c.config.simd_enabled), 564 inst!(I32x4TruncSatF32x4U, (v128) -> v128, |c| c.config.simd_enabled), 565 inst!(F32x4ConvertI32x4S, (v128) -> v128, |c| c.config.simd_enabled), 566 inst!(F32x4ConvertI32x4U, (v128) -> v128, |c| c.config.simd_enabled), 567 inst!(I32x4TruncSatF64x2SZero, (v128) -> v128, |c| c.config.simd_enabled), 568 inst!(I32x4TruncSatF64x2UZero, (v128) -> v128, |c| c.config.simd_enabled), 569 inst!(F64x2ConvertLowI32x4S, (v128) -> v128, |c| c.config.simd_enabled), 570 inst!(F64x2ConvertLowI32x4U, (v128) -> v128, |c| c.config.simd_enabled), 571 inst!(F32x4DemoteF64x2Zero, (v128) -> v128, |c| c.config.simd_enabled), 572 inst!(F64x2PromoteLowF32x4, (v128) -> v128, |c| c.config.simd_enabled), 573 // wide arithmetic 574 inst!(I64Add128, (i64, i64, i64, i64) -> (i64, i64), |c| c.config.wide_arithmetic_enabled && c.config.multi_value_enabled), 575 inst!(I64Sub128, (i64, i64, i64, i64) -> (i64, i64), |c| c.config.wide_arithmetic_enabled && c.config.multi_value_enabled), 576 inst!(I64MulWideS, (i64, i64) -> (i64, i64), |c| c.config.wide_arithmetic_enabled && c.config.multi_value_enabled), 577 inst!(I64MulWideU, (i64, i64) -> (i64, i64), |c| c.config.wide_arithmetic_enabled && c.config.multi_value_enabled), 578 ]; 579 580 #[cfg(test)] 581 mod test { 582 use super::*; 583 584 #[test] sanity()585 fn sanity() { 586 let sut = SingleInstModule { 587 instruction: Instruction::I32Add, 588 parameters: &[ValType::I32, ValType::I32], 589 results: &[ValType::I32], 590 feature: |_| true, 591 canonicalize_nan: None, 592 }; 593 let wasm = sut.to_bytes(); 594 let wat = wasmprinter::print_bytes(wasm).unwrap(); 595 assert_eq!( 596 wat, 597 r#"(module 598 (type (;0;) (func (param i32 i32) (result i32))) 599 (export "test" (func 0)) 600 (func (;0;) (type 0) (param i32 i32) (result i32) 601 local.get 0 602 local.get 1 603 i32.add 604 ) 605 ) 606 "# 607 ) 608 } 609 610 #[test] instructions_encode_to_valid_modules()611 fn instructions_encode_to_valid_modules() { 612 for inst in INSTRUCTIONS { 613 assert!(wat::parse_bytes(&inst.to_bytes()).is_ok()); 614 } 615 } 616 } 617