1 use crate::config::Config; 2 use crate::cranelift_arbitrary::CraneliftArbitrary; 3 use crate::target_isa_extras::TargetIsaExtras; 4 use anyhow::Result; 5 use arbitrary::{Arbitrary, Unstructured}; 6 use cranelift::codegen::data_value::DataValue; 7 use cranelift::codegen::ir::immediates::Offset32; 8 use cranelift::codegen::ir::instructions::{InstructionFormat, ResolvedConstraint}; 9 use cranelift::codegen::ir::stackslot::StackSize; 10 11 use cranelift::codegen::ir::{ 12 types::*, AtomicRmwOp, Block, ConstantData, Endianness, ExternalName, FuncRef, Function, 13 LibCall, Opcode, SigRef, Signature, StackSlot, Type, UserExternalName, UserFuncName, Value, 14 }; 15 use cranelift::codegen::isa::CallConv; 16 use cranelift::frontend::{FunctionBuilder, FunctionBuilderContext, Switch, Variable}; 17 use cranelift::prelude::isa::OwnedTargetIsa; 18 use cranelift::prelude::{ 19 EntityRef, ExtFuncData, FloatCC, InstBuilder, IntCC, JumpTableData, MemFlags, StackSlotData, 20 StackSlotKind, 21 }; 22 use once_cell::sync::Lazy; 23 use std::collections::HashMap; 24 use std::ops::RangeInclusive; 25 use target_lexicon::{Architecture, Triple}; 26 27 type BlockSignature = Vec<Type>; 28 29 fn insert_opcode( 30 fgen: &mut FunctionGenerator, 31 builder: &mut FunctionBuilder, 32 opcode: Opcode, 33 args: &[Type], 34 rets: &[Type], 35 ) -> Result<()> { 36 let mut vals = Vec::with_capacity(args.len()); 37 for &arg in args.into_iter() { 38 let var = fgen.get_variable_of_type(arg)?; 39 let val = builder.use_var(var); 40 vals.push(val); 41 } 42 43 // Some opcodes require us to look at their input arguments to determine the 44 // controlling type. This is not the general case, but we can neatly check this 45 // using `requires_typevar_operand`. 46 let ctrl_type = if opcode.constraints().requires_typevar_operand() { 47 args.first() 48 } else { 49 rets.first() 50 } 51 .copied() 52 .unwrap_or(INVALID); 53 54 // Choose the appropriate instruction format for this opcode 55 let (inst, dfg) = match opcode.format() { 56 InstructionFormat::NullAry => builder.ins().NullAry(opcode, ctrl_type), 57 InstructionFormat::Unary => builder.ins().Unary(opcode, ctrl_type, vals[0]), 58 InstructionFormat::Binary => builder.ins().Binary(opcode, ctrl_type, vals[0], vals[1]), 59 InstructionFormat::Ternary => builder 60 .ins() 61 .Ternary(opcode, ctrl_type, vals[0], vals[1], vals[2]), 62 _ => unimplemented!(), 63 }; 64 let results = dfg.inst_results(inst).to_vec(); 65 66 for (val, &ty) in results.into_iter().zip(rets) { 67 let var = fgen.get_variable_of_type(ty)?; 68 builder.def_var(var, val); 69 } 70 Ok(()) 71 } 72 73 fn insert_call_to_function( 74 fgen: &mut FunctionGenerator, 75 builder: &mut FunctionBuilder, 76 call_opcode: Opcode, 77 sig: &Signature, 78 sig_ref: SigRef, 79 func_ref: FuncRef, 80 ) -> Result<()> { 81 let actuals = fgen.generate_values_for_signature( 82 builder, 83 sig.params.iter().map(|abi_param| abi_param.value_type), 84 )?; 85 86 let addr_ty = fgen.isa.pointer_type(); 87 let call = match call_opcode { 88 Opcode::Call => builder.ins().call(func_ref, &actuals), 89 Opcode::ReturnCall => builder.ins().return_call(func_ref, &actuals), 90 Opcode::CallIndirect => { 91 let addr = builder.ins().func_addr(addr_ty, func_ref); 92 builder.ins().call_indirect(sig_ref, addr, &actuals) 93 } 94 Opcode::ReturnCallIndirect => { 95 let addr = builder.ins().func_addr(addr_ty, func_ref); 96 builder.ins().return_call_indirect(sig_ref, addr, &actuals) 97 } 98 _ => unreachable!(), 99 }; 100 101 // Assign the return values to random variables 102 let ret_values = builder.inst_results(call).to_vec(); 103 let ret_types = sig.returns.iter().map(|p| p.value_type); 104 for (ty, val) in ret_types.zip(ret_values) { 105 let var = fgen.get_variable_of_type(ty)?; 106 builder.def_var(var, val); 107 } 108 109 Ok(()) 110 } 111 112 fn insert_call( 113 fgen: &mut FunctionGenerator, 114 builder: &mut FunctionBuilder, 115 opcode: Opcode, 116 _args: &[Type], 117 _rets: &[Type], 118 ) -> Result<()> { 119 assert!(matches!(opcode, Opcode::Call | Opcode::CallIndirect)); 120 let (sig, sig_ref, func_ref) = fgen.u.choose(&fgen.resources.func_refs)?.clone(); 121 122 insert_call_to_function(fgen, builder, opcode, &sig, sig_ref, func_ref) 123 } 124 125 fn insert_stack_load( 126 fgen: &mut FunctionGenerator, 127 builder: &mut FunctionBuilder, 128 _opcode: Opcode, 129 _args: &[Type], 130 rets: &[Type], 131 ) -> Result<()> { 132 let typevar = rets[0]; 133 let type_size = typevar.bytes(); 134 let (slot, slot_size) = fgen.stack_slot_with_size(type_size)?; 135 let offset = fgen.u.int_in_range(0..=(slot_size - type_size))? as i32; 136 137 let val = builder.ins().stack_load(typevar, slot, offset); 138 let var = fgen.get_variable_of_type(typevar)?; 139 builder.def_var(var, val); 140 141 Ok(()) 142 } 143 144 fn insert_stack_store( 145 fgen: &mut FunctionGenerator, 146 builder: &mut FunctionBuilder, 147 _opcode: Opcode, 148 args: &[Type], 149 _rets: &[Type], 150 ) -> Result<()> { 151 let typevar = args[0]; 152 let type_size = typevar.bytes(); 153 let (slot, slot_size) = fgen.stack_slot_with_size(type_size)?; 154 let offset = fgen.u.int_in_range(0..=(slot_size - type_size))? as i32; 155 156 let arg0 = fgen.get_variable_of_type(typevar)?; 157 let arg0 = builder.use_var(arg0); 158 159 builder.ins().stack_store(arg0, slot, offset); 160 Ok(()) 161 } 162 163 fn insert_cmp( 164 fgen: &mut FunctionGenerator, 165 builder: &mut FunctionBuilder, 166 opcode: Opcode, 167 args: &[Type], 168 rets: &[Type], 169 ) -> Result<()> { 170 let lhs = fgen.get_variable_of_type(args[0])?; 171 let lhs = builder.use_var(lhs); 172 173 let rhs = fgen.get_variable_of_type(args[1])?; 174 let rhs = builder.use_var(rhs); 175 176 let res = if opcode == Opcode::Fcmp { 177 let cc = *fgen.u.choose(FloatCC::all())?; 178 179 // We filter out condition codes that aren't supported by the target at 180 // this point after randomly choosing one, instead of randomly choosing a 181 // supported one, to avoid invalidating the corpus when these get implemented. 182 let unimplemented_cc = match (fgen.isa.triple().architecture, cc) { 183 // Some FloatCC's are not implemented on AArch64, see: 184 // https://github.com/bytecodealliance/wasmtime/issues/4850 185 (Architecture::Aarch64(_), FloatCC::OrderedNotEqual) => true, 186 (Architecture::Aarch64(_), FloatCC::UnorderedOrEqual) => true, 187 (Architecture::Aarch64(_), FloatCC::UnorderedOrLessThan) => true, 188 (Architecture::Aarch64(_), FloatCC::UnorderedOrLessThanOrEqual) => true, 189 (Architecture::Aarch64(_), FloatCC::UnorderedOrGreaterThan) => true, 190 (Architecture::Aarch64(_), FloatCC::UnorderedOrGreaterThanOrEqual) => true, 191 192 // These are not implemented on x86_64, for vectors. 193 (Architecture::X86_64, FloatCC::UnorderedOrEqual | FloatCC::OrderedNotEqual) => { 194 args[0].is_vector() 195 } 196 _ => false, 197 }; 198 if unimplemented_cc { 199 return Err(arbitrary::Error::IncorrectFormat.into()); 200 } 201 202 builder.ins().fcmp(cc, lhs, rhs) 203 } else { 204 let cc = *fgen.u.choose(IntCC::all())?; 205 builder.ins().icmp(cc, lhs, rhs) 206 }; 207 208 let var = fgen.get_variable_of_type(rets[0])?; 209 builder.def_var(var, res); 210 211 Ok(()) 212 } 213 214 fn insert_const( 215 fgen: &mut FunctionGenerator, 216 builder: &mut FunctionBuilder, 217 _opcode: Opcode, 218 _args: &[Type], 219 rets: &[Type], 220 ) -> Result<()> { 221 let typevar = rets[0]; 222 let var = fgen.get_variable_of_type(typevar)?; 223 let val = fgen.generate_const(builder, typevar)?; 224 builder.def_var(var, val); 225 Ok(()) 226 } 227 228 fn insert_bitcast( 229 fgen: &mut FunctionGenerator, 230 builder: &mut FunctionBuilder, 231 args: &[Type], 232 rets: &[Type], 233 ) -> Result<()> { 234 let from_var = fgen.get_variable_of_type(args[0])?; 235 let from_val = builder.use_var(from_var); 236 237 let to_var = fgen.get_variable_of_type(rets[0])?; 238 239 // TODO: We can generate little/big endian flags here. 240 let mut memflags = MemFlags::new(); 241 242 // When bitcasting between vectors of different lane counts, we need to 243 // specify the endianness. 244 if args[0].lane_count() != rets[0].lane_count() { 245 memflags.set_endianness(Endianness::Little); 246 } 247 248 let res = builder.ins().bitcast(rets[0], memflags, from_val); 249 builder.def_var(to_var, res); 250 Ok(()) 251 } 252 253 fn insert_load_store( 254 fgen: &mut FunctionGenerator, 255 builder: &mut FunctionBuilder, 256 opcode: Opcode, 257 args: &[Type], 258 rets: &[Type], 259 ) -> Result<()> { 260 if opcode == Opcode::Bitcast { 261 return insert_bitcast(fgen, builder, args, rets); 262 } 263 264 let ctrl_type = *rets.first().or(args.first()).unwrap(); 265 let type_size = ctrl_type.bytes(); 266 267 let is_atomic = [Opcode::AtomicLoad, Opcode::AtomicStore].contains(&opcode); 268 let (address, flags, offset) = 269 fgen.generate_address_and_memflags(builder, type_size, is_atomic)?; 270 271 // The variable being loaded or stored into 272 let var = fgen.get_variable_of_type(ctrl_type)?; 273 274 match opcode.format() { 275 InstructionFormat::LoadNoOffset => { 276 let (inst, dfg) = builder 277 .ins() 278 .LoadNoOffset(opcode, ctrl_type, flags, address); 279 280 let new_val = dfg.first_result(inst); 281 builder.def_var(var, new_val); 282 } 283 InstructionFormat::StoreNoOffset => { 284 let val = builder.use_var(var); 285 286 builder 287 .ins() 288 .StoreNoOffset(opcode, ctrl_type, flags, val, address); 289 } 290 InstructionFormat::Store => { 291 let val = builder.use_var(var); 292 293 builder 294 .ins() 295 .Store(opcode, ctrl_type, flags, offset, val, address); 296 } 297 InstructionFormat::Load => { 298 let (inst, dfg) = builder 299 .ins() 300 .Load(opcode, ctrl_type, flags, offset, address); 301 302 let new_val = dfg.first_result(inst); 303 builder.def_var(var, new_val); 304 } 305 _ => unimplemented!(), 306 } 307 308 Ok(()) 309 } 310 311 fn insert_atomic_rmw( 312 fgen: &mut FunctionGenerator, 313 builder: &mut FunctionBuilder, 314 _: Opcode, 315 _: &[Type], 316 rets: &[Type], 317 ) -> Result<()> { 318 let ctrl_type = *rets.first().unwrap(); 319 let type_size = ctrl_type.bytes(); 320 321 let rmw_op = *fgen.u.choose(AtomicRmwOp::all())?; 322 323 let (address, flags, offset) = fgen.generate_address_and_memflags(builder, type_size, true)?; 324 325 // AtomicRMW does not directly support offsets, so add the offset to the address separately. 326 let address = builder.ins().iadd_imm(address, i64::from(offset)); 327 328 // Load and store target variables 329 let source_var = fgen.get_variable_of_type(ctrl_type)?; 330 let target_var = fgen.get_variable_of_type(ctrl_type)?; 331 332 let source_val = builder.use_var(source_var); 333 let new_val = builder 334 .ins() 335 .atomic_rmw(ctrl_type, flags, rmw_op, address, source_val); 336 337 builder.def_var(target_var, new_val); 338 Ok(()) 339 } 340 341 fn insert_atomic_cas( 342 fgen: &mut FunctionGenerator, 343 builder: &mut FunctionBuilder, 344 _: Opcode, 345 _: &[Type], 346 rets: &[Type], 347 ) -> Result<()> { 348 let ctrl_type = *rets.first().unwrap(); 349 let type_size = ctrl_type.bytes(); 350 351 let (address, flags, offset) = fgen.generate_address_and_memflags(builder, type_size, true)?; 352 353 // AtomicCas does not directly support offsets, so add the offset to the address separately. 354 let address = builder.ins().iadd_imm(address, i64::from(offset)); 355 356 // Source and Target variables 357 let expected_var = fgen.get_variable_of_type(ctrl_type)?; 358 let store_var = fgen.get_variable_of_type(ctrl_type)?; 359 let loaded_var = fgen.get_variable_of_type(ctrl_type)?; 360 361 let expected_val = builder.use_var(expected_var); 362 let store_val = builder.use_var(store_var); 363 let new_val = builder 364 .ins() 365 .atomic_cas(flags, address, expected_val, store_val); 366 367 builder.def_var(loaded_var, new_val); 368 Ok(()) 369 } 370 371 fn insert_shuffle( 372 fgen: &mut FunctionGenerator, 373 builder: &mut FunctionBuilder, 374 opcode: Opcode, 375 _: &[Type], 376 rets: &[Type], 377 ) -> Result<()> { 378 let ctrl_type = *rets.first().unwrap(); 379 380 let lhs = builder.use_var(fgen.get_variable_of_type(ctrl_type)?); 381 let rhs = builder.use_var(fgen.get_variable_of_type(ctrl_type)?); 382 383 let mask = { 384 let mut lanes = [0u8; 16]; 385 for lane in lanes.iter_mut() { 386 *lane = fgen.u.int_in_range(0..=31)?; 387 } 388 let lanes = ConstantData::from(lanes.as_ref()); 389 builder.func.dfg.immediates.push(lanes) 390 }; 391 392 // This function is called for any `InstructionFormat::Shuffle`. Which today is just 393 // `shuffle`, but lets assert that, just to be sure we don't accidentally insert 394 // something else. 395 assert_eq!(opcode, Opcode::Shuffle); 396 let res = builder.ins().shuffle(lhs, rhs, mask); 397 398 let target_var = fgen.get_variable_of_type(ctrl_type)?; 399 builder.def_var(target_var, res); 400 401 Ok(()) 402 } 403 404 fn insert_ins_ext_lane( 405 fgen: &mut FunctionGenerator, 406 builder: &mut FunctionBuilder, 407 opcode: Opcode, 408 args: &[Type], 409 rets: &[Type], 410 ) -> Result<()> { 411 let vector_type = *args.first().unwrap(); 412 let ret_type = *rets.first().unwrap(); 413 414 let lhs = builder.use_var(fgen.get_variable_of_type(vector_type)?); 415 let max_lane = (vector_type.lane_count() as u8) - 1; 416 let lane = fgen.u.int_in_range(0..=max_lane)?; 417 418 let res = match opcode { 419 Opcode::Insertlane => { 420 let rhs = builder.use_var(fgen.get_variable_of_type(args[1])?); 421 builder.ins().insertlane(lhs, rhs, lane) 422 } 423 Opcode::Extractlane => builder.ins().extractlane(lhs, lane), 424 _ => todo!(), 425 }; 426 427 let target_var = fgen.get_variable_of_type(ret_type)?; 428 builder.def_var(target_var, res); 429 430 Ok(()) 431 } 432 433 type OpcodeInserter = fn( 434 fgen: &mut FunctionGenerator, 435 builder: &mut FunctionBuilder, 436 Opcode, 437 &[Type], 438 &[Type], 439 ) -> Result<()>; 440 441 macro_rules! exceptions { 442 ($op:expr, $args:expr, $rets:expr, $(($($cases:pat),*)),* $(,)?) => { 443 match ($op, $args, $rets) { 444 $( ($($cases,)* ..) => return false, )* 445 _ => true, 446 } 447 } 448 } 449 450 /// Returns true if we believe this `OpcodeSignature` should compile correctly 451 /// for the given target triple. We currently have a range of known issues 452 /// with specific lowerings on specific backends, and we don't want to get 453 /// fuzz bug reports for those. Over time our goal is to eliminate all of these 454 /// exceptions. 455 fn valid_for_target(triple: &Triple, op: Opcode, args: &[Type], rets: &[Type]) -> bool { 456 // Rule out invalid combinations that we don't yet have a good way of rejecting with the 457 // instruction DSL type constraints. 458 match op { 459 Opcode::FcvtToUintSat | Opcode::FcvtToSintSat => { 460 assert_eq!(args.len(), 1); 461 assert_eq!(rets.len(), 1); 462 463 let arg = args[0]; 464 let ret = rets[0]; 465 466 // Vector arguments must produce vector results, and scalar arguments must produce 467 // scalar results. 468 if arg.is_vector() != ret.is_vector() { 469 return false; 470 } 471 472 if arg.is_vector() && ret.is_vector() { 473 // Vector conversions must have the same number of lanes, and the lanes must be the 474 // same bit-width. 475 if arg.lane_count() != ret.lane_count() { 476 return false; 477 } 478 479 if arg.lane_of().bits() != ret.lane_of().bits() { 480 return false; 481 } 482 } 483 } 484 485 Opcode::Bitcast => { 486 assert_eq!(args.len(), 1); 487 assert_eq!(rets.len(), 1); 488 489 let arg = args[0]; 490 let ret = rets[0]; 491 492 // The opcode generator still allows bitcasts between different sized types, but these 493 // are rejected in the verifier. 494 if arg.bits() != ret.bits() { 495 return false; 496 } 497 } 498 499 _ => {} 500 } 501 502 match triple.architecture { 503 Architecture::X86_64 => { 504 exceptions!( 505 op, 506 args, 507 rets, 508 (Opcode::UmulOverflow | Opcode::SmulOverflow, &[I128, I128]), 509 (Opcode::Imul, &[I8X16, I8X16]), 510 // https://github.com/bytecodealliance/wasmtime/issues/5468 511 (Opcode::Smulhi | Opcode::Umulhi, &[I8, I8]), 512 // https://github.com/bytecodealliance/wasmtime/issues/4756 513 (Opcode::Udiv | Opcode::Sdiv, &[I128, I128]), 514 // https://github.com/bytecodealliance/wasmtime/issues/5474 515 (Opcode::Urem | Opcode::Srem, &[I128, I128]), 516 // https://github.com/bytecodealliance/wasmtime/issues/3370 517 ( 518 Opcode::Smin | Opcode::Umin | Opcode::Smax | Opcode::Umax, 519 &[I128, I128] 520 ), 521 // https://github.com/bytecodealliance/wasmtime/issues/5107 522 (Opcode::Cls, &[I8], &[I8]), 523 (Opcode::Cls, &[I16], &[I16]), 524 (Opcode::Cls, &[I32], &[I32]), 525 (Opcode::Cls, &[I64], &[I64]), 526 (Opcode::Cls, &[I128], &[I128]), 527 // TODO 528 (Opcode::Bitselect, &[_, _, _], &[F32 | F64]), 529 // https://github.com/bytecodealliance/wasmtime/issues/4897 530 // https://github.com/bytecodealliance/wasmtime/issues/4899 531 ( 532 Opcode::FcvtToUint 533 | Opcode::FcvtToUintSat 534 | Opcode::FcvtToSint 535 | Opcode::FcvtToSintSat, 536 &[F32 | F64], 537 &[I8 | I16 | I128] 538 ), 539 (Opcode::FcvtToUint | Opcode::FcvtToSint, &[F32X4], &[I32X4]), 540 ( 541 Opcode::FcvtToUint 542 | Opcode::FcvtToUintSat 543 | Opcode::FcvtToSint 544 | Opcode::FcvtToSintSat, 545 &[F64X2], 546 &[I64X2] 547 ), 548 // https://github.com/bytecodealliance/wasmtime/issues/4900 549 (Opcode::FcvtFromUint, &[I128], &[F32 | F64]), 550 // This has a lowering, but only when preceded by `uwiden_low`. 551 (Opcode::FcvtFromUint, &[I64X2], &[F64X2]), 552 // https://github.com/bytecodealliance/wasmtime/issues/4900 553 (Opcode::FcvtFromSint, &[I128], &[F32 | F64]), 554 (Opcode::FcvtFromSint, &[I64X2], &[F64X2]), 555 ( 556 Opcode::Umulhi | Opcode::Smulhi, 557 &([I8X16, I8X16] | [I16X8, I16X8] | [I32X4, I32X4] | [I64X2, I64X2]) 558 ), 559 ( 560 Opcode::UaddSat | Opcode::SaddSat | Opcode::UsubSat | Opcode::SsubSat, 561 &([I32X4, I32X4] | [I64X2, I64X2]) 562 ), 563 (Opcode::Fcopysign, &([F32X4, F32X4] | [F64X2, F64X2])), 564 (Opcode::Popcnt, &([I8X16] | [I16X8] | [I32X4] | [I64X2])), 565 ( 566 Opcode::Umax | Opcode::Smax | Opcode::Umin | Opcode::Smin, 567 &[I64X2, I64X2] 568 ), 569 // https://github.com/bytecodealliance/wasmtime/issues/6104 570 (Opcode::Bitcast, &[I128], &[_]), 571 (Opcode::Bitcast, &[_], &[I128]), 572 (Opcode::Uunarrow), 573 (Opcode::Snarrow | Opcode::Unarrow, &[I64X2, I64X2]), 574 (Opcode::SqmulRoundSat, &[I32X4, I32X4]), 575 // This Icmp is not implemented: #5529 576 (Opcode::Icmp, &[I64X2, I64X2]), 577 // IaddPairwise is implemented, but only for some types, and with some preceding ops. 578 (Opcode::IaddPairwise), 579 // Nothing wrong with this select. But we have an isle rule that can optimize it 580 // into a `min`/`max` instructions, which we don't have implemented yet. 581 (Opcode::Select, &[_, I128, I128]), 582 // These stack accesses can cause segfaults if they are merged into an SSE instruction. 583 // See: #5922 584 ( 585 Opcode::StackStore, 586 &[I8X16 | I16X8 | I32X4 | I64X2 | F32X4 | F64X2] 587 ), 588 ( 589 Opcode::StackLoad, 590 &[], 591 &[I8X16 | I16X8 | I32X4 | I64X2 | F32X4 | F64X2] 592 ), 593 // TODO 594 ( 595 Opcode::Sshr | Opcode::Ushr | Opcode::Ishl, 596 &[I8X16 | I16X8 | I32X4 | I64X2, I128] 597 ), 598 ( 599 Opcode::Rotr | Opcode::Rotl, 600 &[I8X16 | I16X8 | I32X4 | I64X2, _] 601 ), 602 ) 603 } 604 605 Architecture::Aarch64(_) => { 606 exceptions!( 607 op, 608 args, 609 rets, 610 (Opcode::UmulOverflow | Opcode::SmulOverflow, &[I128, I128]), 611 // https://github.com/bytecodealliance/wasmtime/issues/4864 612 (Opcode::Udiv | Opcode::Sdiv, &[I128, I128]), 613 // https://github.com/bytecodealliance/wasmtime/issues/5472 614 (Opcode::Urem | Opcode::Srem, &[I128, I128]), 615 // https://github.com/bytecodealliance/wasmtime/issues/5467 616 (Opcode::Iabs, &[I128]), 617 // https://github.com/bytecodealliance/wasmtime/issues/4313 618 ( 619 Opcode::Smin | Opcode::Umin | Opcode::Smax | Opcode::Umax, 620 &[I128, I128] 621 ), 622 // https://github.com/bytecodealliance/wasmtime/issues/4870 623 (Opcode::Bnot, &[F32 | F64]), 624 ( 625 Opcode::Band 626 | Opcode::Bor 627 | Opcode::Bxor 628 | Opcode::BandNot 629 | Opcode::BorNot 630 | Opcode::BxorNot, 631 &([F32, F32] | [F64, F64]) 632 ), 633 // https://github.com/bytecodealliance/wasmtime/issues/5198 634 (Opcode::Bitselect, &[I128, I128, I128]), 635 // https://github.com/bytecodealliance/wasmtime/issues/4934 636 ( 637 Opcode::FcvtToUint 638 | Opcode::FcvtToUintSat 639 | Opcode::FcvtToSint 640 | Opcode::FcvtToSintSat, 641 &[F32 | F64] 642 ), 643 // https://github.com/bytecodealliance/wasmtime/issues/4933 644 ( 645 Opcode::FcvtFromUint | Opcode::FcvtFromSint, 646 &[I128], 647 &[F32 | F64] 648 ), 649 ( 650 Opcode::Umulhi | Opcode::Smulhi, 651 &([I8X16, I8X16] | [I16X8, I16X8] | [I32X4, I32X4] | [I64X2, I64X2]) 652 ), 653 (Opcode::Popcnt, &[I16X8 | I32X4 | I64X2]), 654 // Nothing wrong with this select. But we have an isle rule that can optimize it 655 // into a `min`/`max` instructions, which we don't have implemented yet. 656 (Opcode::Select, &[I8, I128, I128]), 657 // https://github.com/bytecodealliance/wasmtime/issues/6104 658 (Opcode::Bitcast, &[I128], &[_]), 659 (Opcode::Bitcast, &[_], &[I128]), 660 // TODO 661 ( 662 Opcode::Sshr | Opcode::Ushr | Opcode::Ishl, 663 &[I8X16 | I16X8 | I32X4 | I64X2, I128] 664 ), 665 ( 666 Opcode::Rotr | Opcode::Rotl, 667 &[I8X16 | I16X8 | I32X4 | I64X2, _] 668 ), 669 // TODO 670 (Opcode::Bitselect, &[_, _, _], &[F32 | F64]), 671 (Opcode::VhighBits, &[F32X4 | F64X2]), 672 ) 673 } 674 675 Architecture::S390x => { 676 exceptions!( 677 op, 678 args, 679 rets, 680 (Opcode::UaddOverflow | Opcode::SaddOverflow), 681 (Opcode::UsubOverflow | Opcode::SsubOverflow), 682 (Opcode::UmulOverflow | Opcode::SmulOverflow), 683 ( 684 Opcode::Udiv | Opcode::Sdiv | Opcode::Urem | Opcode::Srem, 685 &[I128, I128] 686 ), 687 (Opcode::Bnot, &[F32 | F64]), 688 ( 689 Opcode::Band 690 | Opcode::Bor 691 | Opcode::Bxor 692 | Opcode::BandNot 693 | Opcode::BorNot 694 | Opcode::BxorNot, 695 &([F32, F32] | [F64, F64]) 696 ), 697 ( 698 Opcode::FcvtToUint 699 | Opcode::FcvtToUintSat 700 | Opcode::FcvtToSint 701 | Opcode::FcvtToSintSat, 702 &[F32 | F64], 703 &[I128] 704 ), 705 ( 706 Opcode::FcvtFromUint | Opcode::FcvtFromSint, 707 &[I128], 708 &[F32 | F64] 709 ), 710 (Opcode::SsubSat | Opcode::SaddSat, &[I64X2, I64X2]), 711 // https://github.com/bytecodealliance/wasmtime/issues/6104 712 (Opcode::Bitcast, &[I128], &[_]), 713 (Opcode::Bitcast, &[_], &[I128]), 714 // TODO 715 (Opcode::Bitselect, &[_, _, _], &[F32 | F64]), 716 ) 717 } 718 719 Architecture::Riscv64(_) => { 720 exceptions!( 721 op, 722 args, 723 rets, 724 // TODO 725 (Opcode::UaddOverflow | Opcode::SaddOverflow), 726 (Opcode::UsubOverflow | Opcode::SsubOverflow), 727 (Opcode::UmulOverflow | Opcode::SmulOverflow), 728 // TODO 729 ( 730 Opcode::Udiv | Opcode::Sdiv | Opcode::Urem | Opcode::Srem, 731 &[I128, I128] 732 ), 733 // TODO 734 (Opcode::Iabs, &[I128]), 735 // TODO 736 (Opcode::Bitselect, &[I128, I128, I128]), 737 // https://github.com/bytecodealliance/wasmtime/issues/5528 738 ( 739 Opcode::FcvtToUint | Opcode::FcvtToSint, 740 [F32 | F64], 741 &[I128] 742 ), 743 ( 744 Opcode::FcvtToUintSat | Opcode::FcvtToSintSat, 745 &[F32 | F64], 746 &[I8 | I16 | I128] 747 ), 748 // https://github.com/bytecodealliance/wasmtime/issues/5528 749 ( 750 Opcode::FcvtFromUint | Opcode::FcvtFromSint, 751 &[I128], 752 &[F32 | F64] 753 ), 754 // https://github.com/bytecodealliance/wasmtime/issues/6104 755 (Opcode::Bitcast, &[I128], &[_]), 756 (Opcode::Bitcast, &[_], &[I128]), 757 // TODO 758 ( 759 Opcode::SelectSpectreGuard, 760 &[_, _, _], 761 &[F32 | F64 | I8X16 | I16X8 | I32X4 | I64X2 | F64X2 | F32X4] 762 ), 763 // TODO 764 (Opcode::Bitselect, &[_, _, _], &[F32 | F64]), 765 ( 766 Opcode::Rotr | Opcode::Rotl, 767 &[I8X16 | I16X8 | I32X4 | I64X2, _] 768 ), 769 ) 770 } 771 772 _ => true, 773 } 774 } 775 776 type OpcodeSignature = (Opcode, Vec<Type>, Vec<Type>); 777 778 static OPCODE_SIGNATURES: Lazy<Vec<OpcodeSignature>> = Lazy::new(|| { 779 let types = &[ 780 I8, I16, I32, I64, I128, // Scalar Integers 781 F32, F64, // Scalar Floats 782 I8X16, I16X8, I32X4, I64X2, // SIMD Integers 783 F32X4, F64X2, // SIMD Floats 784 ]; 785 786 Opcode::all() 787 .iter() 788 .filter(|op| { 789 match op { 790 // Control flow opcodes should not be generated through `generate_instructions`. 791 Opcode::BrTable 792 | Opcode::Brif 793 | Opcode::Jump 794 | Opcode::Return 795 | Opcode::ReturnCall 796 | Opcode::ReturnCallIndirect => false, 797 798 // Constants are generated outside of `generate_instructions` 799 Opcode::Iconst => false, 800 801 // TODO: extract_vector raises exceptions during return type generation becuase it 802 // uses dynamic vectors. 803 Opcode::ExtractVector => false, 804 805 _ => true, 806 } 807 }) 808 .flat_map(|op| { 809 let constraints = op.constraints(); 810 811 let ctrl_types = if let Some(ctrls) = constraints.ctrl_typeset() { 812 Vec::from_iter(types.iter().copied().filter(|ty| ctrls.contains(*ty))) 813 } else { 814 vec![INVALID] 815 }; 816 817 ctrl_types.into_iter().flat_map(move |ctrl_type| { 818 let rets = Vec::from_iter( 819 (0..constraints.num_fixed_results()) 820 .map(|i| constraints.result_type(i, ctrl_type)), 821 ); 822 823 // Cols is a vector whose length will match `num_fixed_value_arguments`, and whose 824 // elements will be vectors of types that are valid for that fixed argument 825 // position. 826 let mut cols = vec![]; 827 828 for i in 0..constraints.num_fixed_value_arguments() { 829 match constraints.value_argument_constraint(i, ctrl_type) { 830 ResolvedConstraint::Bound(ty) => cols.push(Vec::from([ty])), 831 ResolvedConstraint::Free(tys) => cols.push(Vec::from_iter( 832 types.iter().copied().filter(|ty| tys.contains(*ty)), 833 )), 834 } 835 } 836 837 // Generate the cartesian product of cols to produce a vector of argument lists, 838 // argss. The argss vector is seeded with the empty argument list, so there's an 839 // initial value to be extended in the loop below. 840 let mut argss = vec![vec![]]; 841 let mut cols = cols.as_slice(); 842 while let Some((col, rest)) = cols.split_last() { 843 cols = rest; 844 845 let mut next = vec![]; 846 for current in argss.iter() { 847 // Extend the front of each argument candidate with every type in `col`. 848 for ty in col { 849 let mut args = vec![*ty]; 850 args.extend_from_slice(¤t); 851 next.push(args); 852 } 853 } 854 855 let _ = std::mem::replace(&mut argss, next); 856 } 857 858 argss.into_iter().map(move |args| (*op, args, rets.clone())) 859 }) 860 }) 861 .filter(|(op, args, rets)| { 862 // These op/signature combinations need to be vetted 863 exceptions!( 864 op, 865 args.as_slice(), 866 rets.as_slice(), 867 (Opcode::Debugtrap), 868 (Opcode::Trap), 869 (Opcode::Trapz), 870 (Opcode::ResumableTrap), 871 (Opcode::Trapnz), 872 (Opcode::ResumableTrapnz), 873 (Opcode::CallIndirect, &[I32]), 874 (Opcode::FuncAddr), 875 (Opcode::X86Pshufb), 876 (Opcode::AvgRound), 877 (Opcode::Uload8x8), 878 (Opcode::Sload8x8), 879 (Opcode::Uload16x4), 880 (Opcode::Sload16x4), 881 (Opcode::Uload32x2), 882 (Opcode::Sload32x2), 883 (Opcode::StackAddr), 884 (Opcode::DynamicStackLoad), 885 (Opcode::DynamicStackStore), 886 (Opcode::DynamicStackAddr), 887 (Opcode::GlobalValue), 888 (Opcode::SymbolValue), 889 (Opcode::TlsValue), 890 (Opcode::GetPinnedReg), 891 (Opcode::SetPinnedReg), 892 (Opcode::GetFramePointer), 893 (Opcode::GetStackPointer), 894 (Opcode::GetReturnAddress), 895 (Opcode::TableAddr), 896 (Opcode::Null), 897 (Opcode::X86Blendv), 898 (Opcode::IcmpImm), 899 (Opcode::X86Pmulhrsw), 900 (Opcode::IaddImm), 901 (Opcode::ImulImm), 902 (Opcode::UdivImm), 903 (Opcode::SdivImm), 904 (Opcode::UremImm), 905 (Opcode::SremImm), 906 (Opcode::IrsubImm), 907 (Opcode::IaddCin), 908 (Opcode::IaddCarry), 909 (Opcode::UaddOverflowTrap), 910 (Opcode::IsubBin), 911 (Opcode::IsubBorrow), 912 (Opcode::BandImm), 913 (Opcode::BorImm), 914 (Opcode::BxorImm), 915 (Opcode::RotlImm), 916 (Opcode::RotrImm), 917 (Opcode::IshlImm), 918 (Opcode::UshrImm), 919 (Opcode::SshrImm), 920 (Opcode::IsNull), 921 (Opcode::IsInvalid), 922 (Opcode::ScalarToVector), 923 (Opcode::X86Pmaddubsw), 924 (Opcode::X86Cvtt2dq), 925 (Opcode::Umulhi, &[I128, I128], &[I128]), 926 (Opcode::Smulhi, &[I128, I128], &[I128]), 927 // https://github.com/bytecodealliance/wasmtime/issues/6073 928 (Opcode::Iconcat, &[I32, I32], &[I64]), 929 (Opcode::Iconcat, &[I16, I16], &[I32]), 930 (Opcode::Iconcat, &[I8, I8], &[I16]), 931 // https://github.com/bytecodealliance/wasmtime/issues/6073 932 (Opcode::Isplit, &[I64], &[I32, I32]), 933 (Opcode::Isplit, &[I32], &[I16, I16]), 934 (Opcode::Isplit, &[I16], &[I8, I8]), 935 (Opcode::Fmin, &[F32X4, F32X4], &[F32X4]), 936 (Opcode::Fmin, &[F64X2, F64X2], &[F64X2]), 937 (Opcode::Fmax, &[F32X4, F32X4], &[F32X4]), 938 (Opcode::Fmax, &[F64X2, F64X2], &[F64X2]), 939 (Opcode::FcvtToUintSat, &[F32X4], &[I8]), 940 (Opcode::FcvtToUintSat, &[F64X2], &[I8]), 941 (Opcode::FcvtToUintSat, &[F32X4], &[I16]), 942 (Opcode::FcvtToUintSat, &[F64X2], &[I16]), 943 (Opcode::FcvtToUintSat, &[F32X4], &[I32]), 944 (Opcode::FcvtToUintSat, &[F64X2], &[I32]), 945 (Opcode::FcvtToUintSat, &[F32X4], &[I64]), 946 (Opcode::FcvtToUintSat, &[F64X2], &[I64]), 947 (Opcode::FcvtToUintSat, &[F32X4], &[I128]), 948 (Opcode::FcvtToUintSat, &[F64X2], &[I128]), 949 (Opcode::FcvtToUintSat, &[F32], &[I8X16]), 950 (Opcode::FcvtToUintSat, &[F64], &[I8X16]), 951 (Opcode::FcvtToUintSat, &[F32X4], &[I8X16]), 952 (Opcode::FcvtToUintSat, &[F64X2], &[I8X16]), 953 (Opcode::FcvtToUintSat, &[F32], &[I16X8]), 954 (Opcode::FcvtToUintSat, &[F64], &[I16X8]), 955 (Opcode::FcvtToUintSat, &[F32X4], &[I16X8]), 956 (Opcode::FcvtToUintSat, &[F64X2], &[I16X8]), 957 (Opcode::FcvtToUintSat, &[F32], &[I32X4]), 958 (Opcode::FcvtToUintSat, &[F64], &[I32X4]), 959 (Opcode::FcvtToUintSat, &[F64X2], &[I32X4]), 960 (Opcode::FcvtToUintSat, &[F32], &[I64X2]), 961 (Opcode::FcvtToUintSat, &[F64], &[I64X2]), 962 (Opcode::FcvtToUintSat, &[F32X4], &[I64X2]), 963 (Opcode::FcvtToSintSat, &[F32X4], &[I8]), 964 (Opcode::FcvtToSintSat, &[F64X2], &[I8]), 965 (Opcode::FcvtToSintSat, &[F32X4], &[I16]), 966 (Opcode::FcvtToSintSat, &[F64X2], &[I16]), 967 (Opcode::FcvtToSintSat, &[F32X4], &[I32]), 968 (Opcode::FcvtToSintSat, &[F64X2], &[I32]), 969 (Opcode::FcvtToSintSat, &[F32X4], &[I64]), 970 (Opcode::FcvtToSintSat, &[F64X2], &[I64]), 971 (Opcode::FcvtToSintSat, &[F32X4], &[I128]), 972 (Opcode::FcvtToSintSat, &[F64X2], &[I128]), 973 (Opcode::FcvtToSintSat, &[F32], &[I8X16]), 974 (Opcode::FcvtToSintSat, &[F64], &[I8X16]), 975 (Opcode::FcvtToSintSat, &[F32X4], &[I8X16]), 976 (Opcode::FcvtToSintSat, &[F64X2], &[I8X16]), 977 (Opcode::FcvtToSintSat, &[F32], &[I16X8]), 978 (Opcode::FcvtToSintSat, &[F64], &[I16X8]), 979 (Opcode::FcvtToSintSat, &[F32X4], &[I16X8]), 980 (Opcode::FcvtToSintSat, &[F64X2], &[I16X8]), 981 (Opcode::FcvtToSintSat, &[F32], &[I32X4]), 982 (Opcode::FcvtToSintSat, &[F64], &[I32X4]), 983 (Opcode::FcvtToSintSat, &[F64X2], &[I32X4]), 984 (Opcode::FcvtToSintSat, &[F32], &[I64X2]), 985 (Opcode::FcvtToSintSat, &[F64], &[I64X2]), 986 (Opcode::FcvtToSintSat, &[F32X4], &[I64X2]), 987 (Opcode::FcvtFromUint, &[I8X16], &[F32]), 988 (Opcode::FcvtFromUint, &[I16X8], &[F32]), 989 (Opcode::FcvtFromUint, &[I32X4], &[F32]), 990 (Opcode::FcvtFromUint, &[I64X2], &[F32]), 991 (Opcode::FcvtFromUint, &[I8X16], &[F64]), 992 (Opcode::FcvtFromUint, &[I16X8], &[F64]), 993 (Opcode::FcvtFromUint, &[I32X4], &[F64]), 994 (Opcode::FcvtFromUint, &[I64X2], &[F64]), 995 (Opcode::FcvtFromUint, &[I8], &[F32X4]), 996 (Opcode::FcvtFromUint, &[I16], &[F32X4]), 997 (Opcode::FcvtFromUint, &[I32], &[F32X4]), 998 (Opcode::FcvtFromUint, &[I64], &[F32X4]), 999 (Opcode::FcvtFromUint, &[I128], &[F32X4]), 1000 (Opcode::FcvtFromUint, &[I8X16], &[F32X4]), 1001 (Opcode::FcvtFromUint, &[I16X8], &[F32X4]), 1002 (Opcode::FcvtFromUint, &[I64X2], &[F32X4]), 1003 (Opcode::FcvtFromUint, &[I8], &[F64X2]), 1004 (Opcode::FcvtFromUint, &[I16], &[F64X2]), 1005 (Opcode::FcvtFromUint, &[I32], &[F64X2]), 1006 (Opcode::FcvtFromUint, &[I64], &[F64X2]), 1007 (Opcode::FcvtFromUint, &[I128], &[F64X2]), 1008 (Opcode::FcvtFromUint, &[I8X16], &[F64X2]), 1009 (Opcode::FcvtFromUint, &[I16X8], &[F64X2]), 1010 (Opcode::FcvtFromUint, &[I32X4], &[F64X2]), 1011 (Opcode::FcvtFromSint, &[I8X16], &[F32]), 1012 (Opcode::FcvtFromSint, &[I16X8], &[F32]), 1013 (Opcode::FcvtFromSint, &[I32X4], &[F32]), 1014 (Opcode::FcvtFromSint, &[I64X2], &[F32]), 1015 (Opcode::FcvtFromSint, &[I8X16], &[F64]), 1016 (Opcode::FcvtFromSint, &[I16X8], &[F64]), 1017 (Opcode::FcvtFromSint, &[I32X4], &[F64]), 1018 (Opcode::FcvtFromSint, &[I64X2], &[F64]), 1019 (Opcode::FcvtFromSint, &[I8], &[F32X4]), 1020 (Opcode::FcvtFromSint, &[I16], &[F32X4]), 1021 (Opcode::FcvtFromSint, &[I32], &[F32X4]), 1022 (Opcode::FcvtFromSint, &[I64], &[F32X4]), 1023 (Opcode::FcvtFromSint, &[I128], &[F32X4]), 1024 (Opcode::FcvtFromSint, &[I8X16], &[F32X4]), 1025 (Opcode::FcvtFromSint, &[I16X8], &[F32X4]), 1026 (Opcode::FcvtFromSint, &[I64X2], &[F32X4]), 1027 (Opcode::FcvtFromSint, &[I8], &[F64X2]), 1028 (Opcode::FcvtFromSint, &[I16], &[F64X2]), 1029 (Opcode::FcvtFromSint, &[I32], &[F64X2]), 1030 (Opcode::FcvtFromSint, &[I64], &[F64X2]), 1031 (Opcode::FcvtFromSint, &[I128], &[F64X2]), 1032 (Opcode::FcvtFromSint, &[I8X16], &[F64X2]), 1033 (Opcode::FcvtFromSint, &[I16X8], &[F64X2]), 1034 (Opcode::FcvtFromSint, &[I32X4], &[F64X2]), 1035 ) 1036 }) 1037 .collect() 1038 }); 1039 1040 fn inserter_for_format(fmt: InstructionFormat) -> OpcodeInserter { 1041 match fmt { 1042 InstructionFormat::AtomicCas => insert_atomic_cas, 1043 InstructionFormat::AtomicRmw => insert_atomic_rmw, 1044 InstructionFormat::Binary => insert_opcode, 1045 InstructionFormat::BinaryImm64 => todo!(), 1046 InstructionFormat::BinaryImm8 => insert_ins_ext_lane, 1047 InstructionFormat::Call => insert_call, 1048 InstructionFormat::CallIndirect => insert_call, 1049 InstructionFormat::CondTrap => todo!(), 1050 InstructionFormat::DynamicStackLoad => todo!(), 1051 InstructionFormat::DynamicStackStore => todo!(), 1052 InstructionFormat::FloatCompare => insert_cmp, 1053 InstructionFormat::FuncAddr => todo!(), 1054 InstructionFormat::IntAddTrap => todo!(), 1055 InstructionFormat::IntCompare => insert_cmp, 1056 InstructionFormat::IntCompareImm => todo!(), 1057 InstructionFormat::Load => insert_load_store, 1058 InstructionFormat::LoadNoOffset => insert_load_store, 1059 InstructionFormat::NullAry => insert_opcode, 1060 InstructionFormat::Shuffle => insert_shuffle, 1061 InstructionFormat::StackLoad => insert_stack_load, 1062 InstructionFormat::StackStore => insert_stack_store, 1063 InstructionFormat::Store => insert_load_store, 1064 InstructionFormat::StoreNoOffset => insert_load_store, 1065 InstructionFormat::TableAddr => todo!(), 1066 InstructionFormat::Ternary => insert_opcode, 1067 InstructionFormat::TernaryImm8 => insert_ins_ext_lane, 1068 InstructionFormat::Trap => todo!(), 1069 InstructionFormat::Unary => insert_opcode, 1070 InstructionFormat::UnaryConst => insert_const, 1071 InstructionFormat::UnaryGlobalValue => todo!(), 1072 InstructionFormat::UnaryIeee32 => insert_const, 1073 InstructionFormat::UnaryIeee64 => insert_const, 1074 InstructionFormat::UnaryImm => insert_const, 1075 1076 InstructionFormat::BranchTable 1077 | InstructionFormat::Brif 1078 | InstructionFormat::Jump 1079 | InstructionFormat::MultiAry => { 1080 panic!( 1081 "Control-flow instructions should be handled by 'insert_terminator': {:?}", 1082 fmt 1083 ) 1084 } 1085 } 1086 } 1087 1088 pub struct FunctionGenerator<'r, 'data> 1089 where 1090 'data: 'r, 1091 { 1092 u: &'r mut Unstructured<'data>, 1093 config: &'r Config, 1094 resources: Resources, 1095 isa: OwnedTargetIsa, 1096 name: UserFuncName, 1097 signature: Signature, 1098 } 1099 1100 #[derive(Debug, Clone)] 1101 enum BlockTerminator { 1102 Return, 1103 Jump(Block), 1104 Br(Block, Block), 1105 BrTable(Block, Vec<Block>), 1106 Switch(Type, Block, HashMap<u128, Block>), 1107 TailCall(FuncRef), 1108 TailCallIndirect(FuncRef), 1109 } 1110 1111 #[derive(Debug, Clone)] 1112 enum BlockTerminatorKind { 1113 Return, 1114 Jump, 1115 Br, 1116 BrTable, 1117 Switch, 1118 TailCall, 1119 TailCallIndirect, 1120 } 1121 1122 #[derive(Default)] 1123 struct Resources { 1124 vars: HashMap<Type, Vec<Variable>>, 1125 blocks: Vec<(Block, BlockSignature)>, 1126 blocks_without_params: Vec<Block>, 1127 block_terminators: Vec<BlockTerminator>, 1128 func_refs: Vec<(Signature, SigRef, FuncRef)>, 1129 stack_slots: Vec<(StackSlot, StackSize)>, 1130 usercalls: Vec<(UserExternalName, Signature)>, 1131 libcalls: Vec<LibCall>, 1132 } 1133 1134 impl Resources { 1135 /// Partitions blocks at `block`. Only blocks that can be targeted by branches are considered. 1136 /// 1137 /// The first slice includes all blocks up to and including `block`. 1138 /// The second slice includes all remaining blocks. 1139 fn partition_target_blocks( 1140 &self, 1141 block: Block, 1142 ) -> (&[(Block, BlockSignature)], &[(Block, BlockSignature)]) { 1143 // Blocks are stored in-order and have no gaps, this means that we can simply index them by 1144 // their number. We also need to exclude the entry block since it isn't a valid target. 1145 let target_blocks = &self.blocks[1..]; 1146 target_blocks.split_at(block.as_u32() as usize) 1147 } 1148 1149 /// Returns blocks forward of `block`. Only blocks that can be targeted by branches are considered. 1150 fn forward_blocks(&self, block: Block) -> &[(Block, BlockSignature)] { 1151 let (_, forward_blocks) = self.partition_target_blocks(block); 1152 forward_blocks 1153 } 1154 1155 /// Generates a slice of `blocks_without_params` ahead of `block` 1156 fn forward_blocks_without_params(&self, block: Block) -> &[Block] { 1157 let partition_point = self.blocks_without_params.partition_point(|b| *b <= block); 1158 &self.blocks_without_params[partition_point..] 1159 } 1160 1161 /// Generates an iterator of all valid tail call targets. This includes all functions with both 1162 /// the `tail` calling convention and the same return values as the caller. 1163 fn tail_call_targets<'a>( 1164 &'a self, 1165 caller_sig: &'a Signature, 1166 ) -> impl Iterator<Item = &'a (Signature, SigRef, FuncRef)> { 1167 self.func_refs.iter().filter(|(sig, _, _)| { 1168 sig.call_conv == CallConv::Tail && sig.returns == caller_sig.returns 1169 }) 1170 } 1171 } 1172 1173 impl<'r, 'data> FunctionGenerator<'r, 'data> 1174 where 1175 'data: 'r, 1176 { 1177 pub fn new( 1178 u: &'r mut Unstructured<'data>, 1179 config: &'r Config, 1180 isa: OwnedTargetIsa, 1181 name: UserFuncName, 1182 signature: Signature, 1183 usercalls: Vec<(UserExternalName, Signature)>, 1184 libcalls: Vec<LibCall>, 1185 ) -> Self { 1186 Self { 1187 u, 1188 config, 1189 resources: Resources { 1190 usercalls, 1191 libcalls, 1192 ..Resources::default() 1193 }, 1194 isa, 1195 name, 1196 signature, 1197 } 1198 } 1199 1200 /// Generates a random value for config `param` 1201 fn param(&mut self, param: &RangeInclusive<usize>) -> Result<usize> { 1202 Ok(self.u.int_in_range(param.clone())?) 1203 } 1204 1205 fn system_callconv(&mut self) -> CallConv { 1206 // TODO: This currently only runs on linux, so this is the only choice 1207 // We should improve this once we generate flags and targets 1208 CallConv::SystemV 1209 } 1210 1211 /// Finds a stack slot with size of at least n bytes 1212 fn stack_slot_with_size(&mut self, n: u32) -> Result<(StackSlot, StackSize)> { 1213 let first = self 1214 .resources 1215 .stack_slots 1216 .partition_point(|&(_slot, size)| size < n); 1217 Ok(*self.u.choose(&self.resources.stack_slots[first..])?) 1218 } 1219 1220 /// Generates an address that should allow for a store or a load. 1221 /// 1222 /// Addresses aren't generated like other values. They are never stored in variables so that 1223 /// we don't run the risk of returning them from a function, which would make the fuzzer 1224 /// complain since they are different from the interpreter to the backend. 1225 /// 1226 /// `min_size`: Controls the amount of space that the address should have. 1227 /// 1228 /// `aligned`: When passed as true, the resulting address is guaranteed to be aligned 1229 /// on an 8 byte boundary. 1230 /// 1231 /// Returns a valid address and the maximum possible offset that still respects `min_size`. 1232 fn generate_load_store_address( 1233 &mut self, 1234 builder: &mut FunctionBuilder, 1235 min_size: u32, 1236 aligned: bool, 1237 ) -> Result<(Value, u32)> { 1238 // TODO: Currently our only source of addresses is stack_addr, but we 1239 // should add global_value, symbol_value eventually 1240 let (addr, available_size) = { 1241 let (ss, slot_size) = self.stack_slot_with_size(min_size)?; 1242 1243 // stack_slot_with_size guarantees that slot_size >= min_size 1244 let max_offset = slot_size - min_size; 1245 let offset = if aligned { 1246 self.u.int_in_range(0..=max_offset / min_size)? * min_size 1247 } else { 1248 self.u.int_in_range(0..=max_offset)? 1249 }; 1250 1251 let base_addr = builder.ins().stack_addr(I64, ss, offset as i32); 1252 let available_size = slot_size.saturating_sub(offset); 1253 (base_addr, available_size) 1254 }; 1255 1256 // TODO: Insert a bunch of amode opcodes here to modify the address! 1257 1258 // Now that we have an address and a size, we just choose a random offset to return to the 1259 // caller. Preserving min_size bytes. 1260 let max_offset = available_size.saturating_sub(min_size); 1261 Ok((addr, max_offset)) 1262 } 1263 1264 // Generates an address and memflags for a load or store. 1265 fn generate_address_and_memflags( 1266 &mut self, 1267 builder: &mut FunctionBuilder, 1268 min_size: u32, 1269 is_atomic: bool, 1270 ) -> Result<(Value, MemFlags, Offset32)> { 1271 // Should we generate an aligned address 1272 // Some backends have issues with unaligned atomics. 1273 // AArch64: https://github.com/bytecodealliance/wasmtime/issues/5483 1274 // RISCV: https://github.com/bytecodealliance/wasmtime/issues/5882 1275 let requires_aligned_atomics = matches!( 1276 self.isa.triple().architecture, 1277 Architecture::Aarch64(_) | Architecture::Riscv64(_) 1278 ); 1279 let aligned = if is_atomic && requires_aligned_atomics { 1280 true 1281 } else if min_size > 8 { 1282 // TODO: We currently can't guarantee that a stack_slot will be aligned on a 16 byte 1283 // boundary. We don't have a way to specify alignment when creating stack slots, and 1284 // cranelift only guarantees 8 byte alignment between stack slots. 1285 // See: https://github.com/bytecodealliance/wasmtime/issues/5922#issuecomment-1457926624 1286 false 1287 } else { 1288 bool::arbitrary(self.u)? 1289 }; 1290 1291 let mut flags = MemFlags::new(); 1292 // Even if we picked an aligned address, we can always generate unaligned memflags 1293 if aligned && bool::arbitrary(self.u)? { 1294 flags.set_aligned(); 1295 } 1296 // If the address is aligned, then we know it won't trap 1297 if aligned && bool::arbitrary(self.u)? { 1298 flags.set_notrap(); 1299 } 1300 1301 let (address, max_offset) = self.generate_load_store_address(builder, min_size, aligned)?; 1302 1303 // Pick an offset to pass into the load/store. 1304 let offset = if aligned { 1305 0 1306 } else { 1307 self.u.int_in_range(0..=max_offset)? as i32 1308 } 1309 .into(); 1310 1311 Ok((address, flags, offset)) 1312 } 1313 1314 /// Get a variable of type `ty` from the current function 1315 fn get_variable_of_type(&mut self, ty: Type) -> Result<Variable> { 1316 let opts = self.resources.vars.get(&ty).map_or(&[][..], Vec::as_slice); 1317 let var = self.u.choose(opts)?; 1318 Ok(*var) 1319 } 1320 1321 /// Generates an instruction(`iconst`/`fconst`/etc...) to introduce a constant value 1322 fn generate_const(&mut self, builder: &mut FunctionBuilder, ty: Type) -> Result<Value> { 1323 Ok(match self.u.datavalue(ty)? { 1324 DataValue::I8(i) => builder.ins().iconst(ty, i as u8 as i64), 1325 DataValue::I16(i) => builder.ins().iconst(ty, i as u16 as i64), 1326 DataValue::I32(i) => builder.ins().iconst(ty, i as u32 as i64), 1327 DataValue::I64(i) => builder.ins().iconst(ty, i as i64), 1328 DataValue::I128(i) => { 1329 let hi = builder.ins().iconst(I64, (i >> 64) as i64); 1330 let lo = builder.ins().iconst(I64, i as i64); 1331 builder.ins().iconcat(lo, hi) 1332 } 1333 DataValue::F32(f) => builder.ins().f32const(f), 1334 DataValue::F64(f) => builder.ins().f64const(f), 1335 DataValue::V128(bytes) => { 1336 let data = bytes.to_vec().into(); 1337 let handle = builder.func.dfg.constants.insert(data); 1338 builder.ins().vconst(ty, handle) 1339 } 1340 _ => unimplemented!(), 1341 }) 1342 } 1343 1344 /// Chooses a random block which can be targeted by a jump / branch. 1345 /// This means any block that is not the first block. 1346 fn generate_target_block(&mut self, source_block: Block) -> Result<Block> { 1347 // We try to mostly generate forward branches to avoid generating an excessive amount of 1348 // infinite loops. But they are still important, so give them a small chance of existing. 1349 let (backwards_blocks, forward_blocks) = 1350 self.resources.partition_target_blocks(source_block); 1351 let ratio = self.config.backwards_branch_ratio; 1352 let block_targets = if !backwards_blocks.is_empty() && self.u.ratio(ratio.0, ratio.1)? { 1353 backwards_blocks 1354 } else { 1355 forward_blocks 1356 }; 1357 assert!(!block_targets.is_empty()); 1358 1359 let (block, _) = self.u.choose(block_targets)?.clone(); 1360 Ok(block) 1361 } 1362 1363 fn generate_values_for_block( 1364 &mut self, 1365 builder: &mut FunctionBuilder, 1366 block: Block, 1367 ) -> Result<Vec<Value>> { 1368 let (_, sig) = self.resources.blocks[block.as_u32() as usize].clone(); 1369 self.generate_values_for_signature(builder, sig.iter().copied()) 1370 } 1371 1372 fn generate_values_for_signature<I: Iterator<Item = Type>>( 1373 &mut self, 1374 builder: &mut FunctionBuilder, 1375 signature: I, 1376 ) -> Result<Vec<Value>> { 1377 signature 1378 .map(|ty| { 1379 let var = self.get_variable_of_type(ty)?; 1380 let val = builder.use_var(var); 1381 Ok(val) 1382 }) 1383 .collect() 1384 } 1385 1386 /// The terminator that we need to insert has already been picked ahead of time 1387 /// we just need to build the instructions for it 1388 fn insert_terminator( 1389 &mut self, 1390 builder: &mut FunctionBuilder, 1391 source_block: Block, 1392 ) -> Result<()> { 1393 let terminator = self.resources.block_terminators[source_block.as_u32() as usize].clone(); 1394 1395 match terminator { 1396 BlockTerminator::Return => { 1397 let types: Vec<Type> = { 1398 let rets = &builder.func.signature.returns; 1399 rets.iter().map(|p| p.value_type).collect() 1400 }; 1401 let vals = self.generate_values_for_signature(builder, types.into_iter())?; 1402 1403 builder.ins().return_(&vals[..]); 1404 } 1405 BlockTerminator::Jump(target) => { 1406 let args = self.generate_values_for_block(builder, target)?; 1407 builder.ins().jump(target, &args[..]); 1408 } 1409 BlockTerminator::Br(left, right) => { 1410 let left_args = self.generate_values_for_block(builder, left)?; 1411 let right_args = self.generate_values_for_block(builder, right)?; 1412 1413 let condbr_types = [I8, I16, I32, I64, I128]; 1414 let _type = *self.u.choose(&condbr_types[..])?; 1415 let val = builder.use_var(self.get_variable_of_type(_type)?); 1416 builder 1417 .ins() 1418 .brif(val, left, &left_args[..], right, &right_args[..]); 1419 } 1420 BlockTerminator::BrTable(default, targets) => { 1421 // Create jump tables on demand 1422 let mut jt = Vec::with_capacity(targets.len()); 1423 for block in targets { 1424 let args = self.generate_values_for_block(builder, block)?; 1425 jt.push(builder.func.dfg.block_call(block, &args)) 1426 } 1427 1428 let args = self.generate_values_for_block(builder, default)?; 1429 let jt_data = JumpTableData::new(builder.func.dfg.block_call(default, &args), &jt); 1430 let jt = builder.create_jump_table(jt_data); 1431 1432 // br_table only supports I32 1433 let val = builder.use_var(self.get_variable_of_type(I32)?); 1434 1435 builder.ins().br_table(val, jt); 1436 } 1437 BlockTerminator::Switch(_type, default, entries) => { 1438 let mut switch = Switch::new(); 1439 for (&entry, &block) in entries.iter() { 1440 switch.set_entry(entry, block); 1441 } 1442 1443 let switch_val = builder.use_var(self.get_variable_of_type(_type)?); 1444 1445 switch.emit(builder, switch_val, default); 1446 } 1447 BlockTerminator::TailCall(target) | BlockTerminator::TailCallIndirect(target) => { 1448 let (sig, sig_ref, func_ref) = self 1449 .resources 1450 .func_refs 1451 .iter() 1452 .find(|(_, _, f)| *f == target) 1453 .expect("Failed to find previously selected function") 1454 .clone(); 1455 1456 let opcode = match terminator { 1457 BlockTerminator::TailCall(_) => Opcode::ReturnCall, 1458 BlockTerminator::TailCallIndirect(_) => Opcode::ReturnCallIndirect, 1459 _ => unreachable!(), 1460 }; 1461 1462 insert_call_to_function(self, builder, opcode, &sig, sig_ref, func_ref)?; 1463 } 1464 } 1465 1466 Ok(()) 1467 } 1468 1469 /// Fills the current block with random instructions 1470 fn generate_instructions(&mut self, builder: &mut FunctionBuilder) -> Result<()> { 1471 for _ in 0..self.param(&self.config.instructions_per_block)? { 1472 let (op, args, rets) = self.u.choose(&OPCODE_SIGNATURES)?; 1473 1474 // We filter out instructions that aren't supported by the target at this point instead 1475 // of building a single vector of valid instructions at the beginning of function 1476 // generation, to avoid invalidating the corpus when instructions are enabled/disabled. 1477 if !valid_for_target(&self.isa.triple(), *op, &args, &rets) { 1478 return Err(arbitrary::Error::IncorrectFormat.into()); 1479 } 1480 1481 let inserter = inserter_for_format(op.format()); 1482 inserter(self, builder, *op, &args, &rets)?; 1483 } 1484 1485 Ok(()) 1486 } 1487 1488 fn generate_funcrefs(&mut self, builder: &mut FunctionBuilder) -> Result<()> { 1489 let usercalls: Vec<(ExternalName, Signature)> = self 1490 .resources 1491 .usercalls 1492 .iter() 1493 .map(|(name, signature)| { 1494 let user_func_ref = builder.func.declare_imported_user_function(name.clone()); 1495 let name = ExternalName::User(user_func_ref); 1496 (name, signature.clone()) 1497 }) 1498 .collect(); 1499 1500 let lib_callconv = self.system_callconv(); 1501 let libcalls: Vec<(ExternalName, Signature)> = self 1502 .resources 1503 .libcalls 1504 .iter() 1505 .map(|libcall| { 1506 let pointer_type = Type::int_with_byte_size( 1507 self.isa.triple().pointer_width().unwrap().bytes().into(), 1508 ) 1509 .unwrap(); 1510 let signature = libcall.signature(lib_callconv, pointer_type); 1511 let name = ExternalName::LibCall(*libcall); 1512 (name, signature) 1513 }) 1514 .collect(); 1515 1516 for (name, signature) in usercalls.into_iter().chain(libcalls) { 1517 let sig_ref = builder.import_signature(signature.clone()); 1518 let func_ref = builder.import_function(ExtFuncData { 1519 name, 1520 signature: sig_ref, 1521 colocated: self.u.arbitrary()?, 1522 }); 1523 1524 self.resources 1525 .func_refs 1526 .push((signature, sig_ref, func_ref)); 1527 } 1528 1529 Ok(()) 1530 } 1531 1532 fn generate_stack_slots(&mut self, builder: &mut FunctionBuilder) -> Result<()> { 1533 for _ in 0..self.param(&self.config.static_stack_slots_per_function)? { 1534 let bytes = self.param(&self.config.static_stack_slot_size)? as u32; 1535 let ss_data = StackSlotData::new(StackSlotKind::ExplicitSlot, bytes); 1536 let slot = builder.create_sized_stack_slot(ss_data); 1537 self.resources.stack_slots.push((slot, bytes)); 1538 } 1539 1540 self.resources 1541 .stack_slots 1542 .sort_unstable_by_key(|&(_slot, bytes)| bytes); 1543 1544 Ok(()) 1545 } 1546 1547 /// Zero initializes the stack slot by inserting `stack_store`'s. 1548 fn initialize_stack_slots(&mut self, builder: &mut FunctionBuilder) -> Result<()> { 1549 let i8_zero = builder.ins().iconst(I8, 0); 1550 let i16_zero = builder.ins().iconst(I16, 0); 1551 let i32_zero = builder.ins().iconst(I32, 0); 1552 let i64_zero = builder.ins().iconst(I64, 0); 1553 let i128_zero = builder.ins().uextend(I128, i64_zero); 1554 1555 for &(slot, init_size) in self.resources.stack_slots.iter() { 1556 let mut size = init_size; 1557 1558 // Insert the largest available store for the remaining size. 1559 while size != 0 { 1560 let offset = (init_size - size) as i32; 1561 let (val, filled) = match size { 1562 sz if sz / 16 > 0 => (i128_zero, 16), 1563 sz if sz / 8 > 0 => (i64_zero, 8), 1564 sz if sz / 4 > 0 => (i32_zero, 4), 1565 sz if sz / 2 > 0 => (i16_zero, 2), 1566 _ => (i8_zero, 1), 1567 }; 1568 builder.ins().stack_store(val, slot, offset); 1569 size -= filled; 1570 } 1571 } 1572 Ok(()) 1573 } 1574 1575 /// Creates a random amount of blocks in this function 1576 fn generate_blocks(&mut self, builder: &mut FunctionBuilder) -> Result<()> { 1577 let extra_block_count = self.param(&self.config.blocks_per_function)?; 1578 1579 // We must always have at least one block, so we generate the "extra" blocks and add 1 for 1580 // the entry block. 1581 let block_count = 1 + extra_block_count; 1582 1583 // Blocks need to be sorted in ascending order 1584 self.resources.blocks = (0..block_count) 1585 .map(|i| { 1586 let is_entry = i == 0; 1587 let block = builder.create_block(); 1588 1589 // Optionally mark blocks that are not the entry block as cold 1590 if !is_entry { 1591 if bool::arbitrary(self.u)? { 1592 builder.set_cold_block(block); 1593 } 1594 } 1595 1596 // The first block has to have the function signature, but for the rest of them we generate 1597 // a random signature; 1598 if is_entry { 1599 builder.append_block_params_for_function_params(block); 1600 Ok(( 1601 block, 1602 self.signature.params.iter().map(|a| a.value_type).collect(), 1603 )) 1604 } else { 1605 let sig = self.generate_block_signature()?; 1606 sig.iter().for_each(|ty| { 1607 builder.append_block_param(block, *ty); 1608 }); 1609 Ok((block, sig)) 1610 } 1611 }) 1612 .collect::<Result<Vec<_>>>()?; 1613 1614 // Valid blocks for jump tables have to have no parameters in the signature, and must also 1615 // not be the first block. 1616 self.resources.blocks_without_params = self.resources.blocks[1..] 1617 .iter() 1618 .filter(|(_, sig)| sig.len() == 0) 1619 .map(|(b, _)| *b) 1620 .collect(); 1621 1622 // Compute the block CFG 1623 // 1624 // cranelift-frontend requires us to never generate unreachable blocks 1625 // To ensure this property we start by constructing a main "spine" of blocks. So block1 can 1626 // always jump to block2, and block2 can always jump to block3, etc... 1627 // 1628 // That is not a very interesting CFG, so we introduce variations on that, but always 1629 // ensuring that the property of pointing to the next block is maintained whatever the 1630 // branching mechanism we use. 1631 let blocks = self.resources.blocks.clone(); 1632 self.resources.block_terminators = blocks 1633 .iter() 1634 .map(|&(block, _)| { 1635 let next_block = Block::with_number(block.as_u32() + 1).unwrap(); 1636 let forward_blocks = self.resources.forward_blocks(block); 1637 let paramless_targets = self.resources.forward_blocks_without_params(block); 1638 let has_paramless_targets = !paramless_targets.is_empty(); 1639 let next_block_is_paramless = paramless_targets.contains(&next_block); 1640 1641 let mut valid_terminators = vec![]; 1642 1643 if forward_blocks.is_empty() { 1644 // Return is only valid on the last block. 1645 valid_terminators.push(BlockTerminatorKind::Return); 1646 } else { 1647 // If we have more than one block we can allow terminators that target blocks. 1648 // TODO: We could add some kind of BrReturn here, to explore edges where we 1649 // exit in the middle of the function 1650 valid_terminators.extend_from_slice(&[ 1651 BlockTerminatorKind::Jump, 1652 BlockTerminatorKind::Br, 1653 BlockTerminatorKind::BrTable, 1654 ]); 1655 } 1656 1657 // As the Switch interface only allows targeting blocks without params we need 1658 // to ensure that the next block has no params, since that one is guaranteed to be 1659 // picked in either case. 1660 if has_paramless_targets && next_block_is_paramless { 1661 valid_terminators.push(BlockTerminatorKind::Switch); 1662 } 1663 1664 // Tail Calls are a block terminator, so we should insert them as any other block 1665 // terminator. We should ensure that we can select at least one target before considering 1666 // them as candidate instructions. 1667 let has_tail_callees = self 1668 .resources 1669 .tail_call_targets(&self.signature) 1670 .next() 1671 .is_some(); 1672 let is_tail_caller = self.signature.call_conv == CallConv::Tail; 1673 1674 let supports_tail_calls = match self.isa.triple().architecture { 1675 Architecture::Aarch64(_) | Architecture::Riscv64(_) => true, 1676 // TODO: x64 currently requires frame pointers for tail calls. 1677 Architecture::X86_64 => self.isa.flags().preserve_frame_pointers(), 1678 // TODO: Other platforms do not support tail calls yet. 1679 _ => false, 1680 }; 1681 1682 if is_tail_caller && has_tail_callees && supports_tail_calls { 1683 valid_terminators.extend([ 1684 BlockTerminatorKind::TailCall, 1685 BlockTerminatorKind::TailCallIndirect, 1686 ]); 1687 } 1688 1689 let terminator = self.u.choose(&valid_terminators)?; 1690 1691 // Choose block targets for the terminators that we picked above 1692 Ok(match terminator { 1693 BlockTerminatorKind::Return => BlockTerminator::Return, 1694 BlockTerminatorKind::Jump => BlockTerminator::Jump(next_block), 1695 BlockTerminatorKind::Br => { 1696 BlockTerminator::Br(next_block, self.generate_target_block(block)?) 1697 } 1698 // TODO: Allow generating backwards branches here 1699 BlockTerminatorKind::BrTable => { 1700 // Make the default the next block, and then we don't have to worry 1701 // that we can reach it via the targets 1702 let default = next_block; 1703 1704 let target_count = self.param(&self.config.jump_table_entries)?; 1705 let targets = Result::from_iter( 1706 (0..target_count).map(|_| self.generate_target_block(block)), 1707 )?; 1708 1709 BlockTerminator::BrTable(default, targets) 1710 } 1711 BlockTerminatorKind::Switch => { 1712 // Make the default the next block, and then we don't have to worry 1713 // that we can reach it via the entries below 1714 let default_block = next_block; 1715 1716 let _type = *self.u.choose(&[I8, I16, I32, I64, I128][..])?; 1717 1718 // Build this into a HashMap since we cannot have duplicate entries. 1719 let mut entries = HashMap::new(); 1720 for _ in 0..self.param(&self.config.switch_cases)? { 1721 // The Switch API only allows for entries that are addressable by the index type 1722 // so we need to limit the range of values that we generate. 1723 let (ty_min, ty_max) = _type.bounds(false); 1724 let range_start = self.u.int_in_range(ty_min..=ty_max)?; 1725 1726 // We can either insert a contiguous range of blocks or a individual block 1727 // This is done because the Switch API specializes contiguous ranges. 1728 let range_size = if bool::arbitrary(self.u)? { 1729 1 1730 } else { 1731 self.param(&self.config.switch_max_range_size)? 1732 } as u128; 1733 1734 // Build the switch entries 1735 for i in 0..range_size { 1736 let index = range_start.wrapping_add(i) % ty_max; 1737 let block = *self 1738 .u 1739 .choose(self.resources.forward_blocks_without_params(block))?; 1740 1741 entries.insert(index, block); 1742 } 1743 } 1744 1745 BlockTerminator::Switch(_type, default_block, entries) 1746 } 1747 BlockTerminatorKind::TailCall => { 1748 let targets = self 1749 .resources 1750 .tail_call_targets(&self.signature) 1751 .collect::<Vec<_>>(); 1752 let (_, _, funcref) = *self.u.choose(&targets[..])?; 1753 BlockTerminator::TailCall(*funcref) 1754 } 1755 BlockTerminatorKind::TailCallIndirect => { 1756 let targets = self 1757 .resources 1758 .tail_call_targets(&self.signature) 1759 .collect::<Vec<_>>(); 1760 let (_, _, funcref) = *self.u.choose(&targets[..])?; 1761 BlockTerminator::TailCallIndirect(*funcref) 1762 } 1763 }) 1764 }) 1765 .collect::<Result<_>>()?; 1766 1767 Ok(()) 1768 } 1769 1770 fn generate_block_signature(&mut self) -> Result<BlockSignature> { 1771 let param_count = self.param(&self.config.block_signature_params)?; 1772 1773 let mut params = Vec::with_capacity(param_count); 1774 for _ in 0..param_count { 1775 params.push(self.u._type((&*self.isa).supports_simd())?); 1776 } 1777 Ok(params) 1778 } 1779 1780 fn build_variable_pool(&mut self, builder: &mut FunctionBuilder) -> Result<()> { 1781 let block = builder.current_block().unwrap(); 1782 1783 // Define variables for the function signature 1784 let mut vars: Vec<_> = builder 1785 .func 1786 .signature 1787 .params 1788 .iter() 1789 .map(|param| param.value_type) 1790 .zip(builder.block_params(block).iter().copied()) 1791 .collect(); 1792 1793 // Create a pool of vars that are going to be used in this function 1794 for _ in 0..self.param(&self.config.vars_per_function)? { 1795 let ty = self.u._type((&*self.isa).supports_simd())?; 1796 let value = self.generate_const(builder, ty)?; 1797 vars.push((ty, value)); 1798 } 1799 1800 for (id, (ty, value)) in vars.into_iter().enumerate() { 1801 let var = Variable::new(id); 1802 builder.declare_var(var, ty); 1803 builder.def_var(var, value); 1804 self.resources 1805 .vars 1806 .entry(ty) 1807 .or_insert_with(Vec::new) 1808 .push(var); 1809 } 1810 1811 Ok(()) 1812 } 1813 1814 /// We generate a function in multiple stages: 1815 /// 1816 /// * First we generate a random number of empty blocks 1817 /// * Then we generate a random pool of variables to be used throughout the function 1818 /// * We then visit each block and generate random instructions 1819 /// 1820 /// Because we generate all blocks and variables up front we already know everything that 1821 /// we need when generating instructions (i.e. jump targets / variables) 1822 pub fn generate(mut self) -> Result<Function> { 1823 let mut fn_builder_ctx = FunctionBuilderContext::new(); 1824 let mut func = Function::with_name_signature(self.name.clone(), self.signature.clone()); 1825 1826 let mut builder = FunctionBuilder::new(&mut func, &mut fn_builder_ctx); 1827 1828 // Build the function references before generating the block CFG since we store 1829 // function references in the CFG. 1830 self.generate_funcrefs(&mut builder)?; 1831 self.generate_blocks(&mut builder)?; 1832 1833 // Function preamble 1834 self.generate_stack_slots(&mut builder)?; 1835 1836 // Main instruction generation loop 1837 for (block, block_sig) in self.resources.blocks.clone().into_iter() { 1838 let is_block0 = block.as_u32() == 0; 1839 builder.switch_to_block(block); 1840 1841 if is_block0 { 1842 // The first block is special because we must create variables both for the 1843 // block signature and for the variable pool. Additionally, we must also define 1844 // initial values for all variables that are not the function signature. 1845 self.build_variable_pool(&mut builder)?; 1846 1847 // Stack slots have random bytes at the beginning of the function 1848 // initialize them to a constant value so that execution stays predictable. 1849 self.initialize_stack_slots(&mut builder)?; 1850 } else { 1851 // Define variables for the block params 1852 for (i, ty) in block_sig.iter().enumerate() { 1853 let var = self.get_variable_of_type(*ty)?; 1854 let block_param = builder.block_params(block)[i]; 1855 builder.def_var(var, block_param); 1856 } 1857 } 1858 1859 // Generate block instructions 1860 self.generate_instructions(&mut builder)?; 1861 1862 // Insert a terminator to safely exit the block 1863 self.insert_terminator(&mut builder, block)?; 1864 } 1865 1866 builder.seal_all_blocks(); 1867 builder.finalize(); 1868 1869 Ok(func) 1870 } 1871 } 1872