1 //! Operations for the `gc` operations. 2 3 use crate::generators::gc_ops::types::StackType; 4 use crate::generators::gc_ops::{ 5 limits::GcOpsLimits, 6 types::{CompositeType, RecGroupId, StructType, TypeId, Types}, 7 }; 8 use serde::{Deserialize, Serialize}; 9 use std::collections::BTreeMap; 10 use wasm_encoder::{ 11 CodeSection, ConstExpr, EntityType, ExportKind, ExportSection, Function, FunctionSection, 12 GlobalSection, ImportSection, Instruction, Module, RefType, TableSection, TableType, 13 TypeSection, ValType, 14 }; 15 16 /// The base offsets and indices for various Wasm entities within 17 /// their index spaces in the the encoded Wasm binary. 18 #[derive(Clone, Copy)] 19 struct WasmEncodingBases { 20 struct_type_base: u32, 21 typed_first_func_index: u32, 22 struct_local_idx: u32, 23 typed_local_base: u32, 24 struct_global_idx: u32, 25 typed_global_base: u32, 26 struct_table_idx: u32, 27 typed_table_base: u32, 28 } 29 30 /// A description of a Wasm module that makes a series of `externref` table 31 /// operations. 32 #[derive(Debug, Default, Serialize, Deserialize)] 33 pub struct GcOps { 34 pub(crate) limits: GcOpsLimits, 35 pub(crate) ops: Vec<GcOp>, 36 pub(crate) types: Types, 37 } 38 39 impl GcOps { 40 /// Serialize this module into a Wasm binary. 41 /// 42 /// The module requires several function imports. See this function's 43 /// implementation for their exact types. 44 /// 45 /// The single export of the module is a function "run" that takes 46 /// `self.num_params` parameters of type `externref`. 47 /// 48 /// The "run" function does not terminate; you should run it with limited 49 /// fuel. It also is not guaranteed to avoid traps: it may access 50 /// out-of-bounds of the table. to_wasm_binary(&mut self) -> Vec<u8>51 pub fn to_wasm_binary(&mut self) -> Vec<u8> { 52 self.fixup(); 53 54 let mut module = Module::new(); 55 56 // Encode the types for all functions that we are using. 57 let mut types = TypeSection::new(); 58 59 // 0: "gc" 60 types.ty().function( 61 vec![], 62 // Return a bunch of stuff from `gc` so that we exercise GCing when 63 // there is return pointer space allocated on the stack. This is 64 // especially important because the x64 backend currently 65 // dynamically adjusts the stack pointer for each call that uses 66 // return pointers rather than statically allocating space in the 67 // stack frame. 68 vec![ValType::EXTERNREF, ValType::EXTERNREF, ValType::EXTERNREF], 69 ); 70 71 // 1: "run" 72 let mut params: Vec<ValType> = Vec::with_capacity( 73 usize::try_from(self.limits.num_params).expect("num_params is too large"), 74 ); 75 for _i in 0..self.limits.num_params { 76 params.push(ValType::EXTERNREF); 77 } 78 let params_len = 79 u32::try_from(params.len()).expect("params len should be within u32 range"); 80 let results = vec![]; 81 types.ty().function(params, results); 82 83 // 2: `take_refs` 84 types.ty().function( 85 vec![ValType::EXTERNREF, ValType::EXTERNREF, ValType::EXTERNREF], 86 vec![], 87 ); 88 89 // 3: `make_refs` 90 types.ty().function( 91 vec![], 92 vec![ValType::EXTERNREF, ValType::EXTERNREF, ValType::EXTERNREF], 93 ); 94 95 // 4: `take_struct` 96 types.ty().function( 97 vec![ValType::Ref(RefType { 98 nullable: true, 99 heap_type: wasm_encoder::HeapType::Abstract { 100 shared: false, 101 ty: wasm_encoder::AbstractHeapType::Struct, 102 }, 103 })], 104 vec![], 105 ); 106 107 let struct_type_base: u32 = types.len(); 108 109 // Sort all types globally so supertypes come before subtypes. 110 // This is used to order types *within* each rec group. 111 let mut type_order = Vec::new(); 112 self.types.sort_types_topo(&mut type_order); 113 114 // Build a position map so we can sort each group's members 115 // according to the global type order. 116 let type_position: BTreeMap<TypeId, usize> = type_order 117 .iter() 118 .enumerate() 119 .map(|(i, &id)| (id, i)) 120 .collect(); 121 122 // Topological sort of rec groups: if a type in group G has a 123 // supertype in group H, then H appears before G. 124 let mut group_order = Vec::new(); 125 self.types.sort_rec_groups_topo(&mut group_order); 126 127 // For each group, collect its members sorted by the global type order. 128 let mut group_members: BTreeMap<RecGroupId, Vec<TypeId>> = BTreeMap::new(); 129 for &gid in &group_order { 130 if let Some(member_set) = self.types.rec_groups.get(&gid) { 131 let mut members: Vec<TypeId> = member_set.iter().copied().collect(); 132 members.sort_by_key(|tid| type_position.get(tid).copied().unwrap_or(usize::MAX)); 133 group_members.insert(gid, members); 134 } 135 } 136 137 // Build the type-id-to-wasm-index map directly. 138 let mut type_ids_to_index: BTreeMap<TypeId, u32> = BTreeMap::new(); 139 let mut next_idx = struct_type_base; 140 for g in &group_order { 141 if let Some(members) = group_members.get(g) { 142 for &tid in members { 143 type_ids_to_index.insert(tid, next_idx); 144 next_idx += 1; 145 } 146 } 147 } 148 149 let encode_ty_id = |ty_id: &TypeId| -> wasm_encoder::SubType { 150 let def = &self.types.type_defs[ty_id]; 151 match &def.composite_type { 152 CompositeType::Struct(StructType {}) => wasm_encoder::SubType { 153 is_final: def.is_final, 154 supertype_idx: def.supertype.map(|st| type_ids_to_index[&st]), 155 composite_type: wasm_encoder::CompositeType { 156 inner: wasm_encoder::CompositeInnerType::Struct(wasm_encoder::StructType { 157 fields: Box::new([]), 158 }), 159 shared: false, 160 describes: None, 161 descriptor: None, 162 }, 163 }, 164 } 165 }; 166 167 let mut struct_count = 0; 168 169 // Emit rec groups in the derived order. 170 for g in &group_order { 171 let type_ids = group_members.get(g).map(|v| v.as_slice()).unwrap_or(&[]); 172 let members: Vec<wasm_encoder::SubType> = type_ids.iter().map(encode_ty_id).collect(); 173 types.ty().rec(members); 174 struct_count += u32::try_from(type_ids.len()).unwrap(); 175 } 176 177 let typed_fn_type_base: u32 = struct_type_base + struct_count; 178 179 for i in 0..struct_count { 180 let concrete = struct_type_base + i; 181 types.ty().function( 182 vec![ValType::Ref(RefType { 183 nullable: true, 184 heap_type: wasm_encoder::HeapType::Concrete(concrete), 185 })], 186 vec![], 187 ); 188 } 189 190 // Import the GC function. 191 let mut imports = ImportSection::new(); 192 imports.import("", "gc", EntityType::Function(0)); 193 imports.import("", "take_refs", EntityType::Function(2)); 194 imports.import("", "make_refs", EntityType::Function(3)); 195 imports.import("", "take_struct", EntityType::Function(4)); 196 197 // For each of our concrete struct types, define a function 198 // import that takes an argument of that concrete type. 199 let typed_first_func_index: u32 = imports.len(); 200 201 for i in 0..struct_count { 202 let ty_idx = typed_fn_type_base + i; 203 let name = format!("take_struct_{}", struct_type_base + i); 204 imports.import("", &name, EntityType::Function(ty_idx)); 205 } 206 207 // Define our table. 208 let mut tables = TableSection::new(); 209 tables.table(TableType { 210 element_type: RefType::EXTERNREF, 211 minimum: u64::from(self.limits.table_size), 212 maximum: None, 213 table64: false, 214 shared: false, 215 }); 216 217 let struct_table_idx = tables.len(); 218 tables.table(TableType { 219 element_type: RefType { 220 nullable: true, 221 heap_type: wasm_encoder::HeapType::Abstract { 222 shared: false, 223 ty: wasm_encoder::AbstractHeapType::Struct, 224 }, 225 }, 226 minimum: u64::from(self.limits.table_size), 227 maximum: None, 228 table64: false, 229 shared: false, 230 }); 231 232 let typed_table_base = tables.len(); 233 for i in 0..struct_count { 234 let concrete = struct_type_base + i; 235 tables.table(TableType { 236 element_type: RefType { 237 nullable: true, 238 heap_type: wasm_encoder::HeapType::Concrete(concrete), 239 }, 240 minimum: u64::from(self.limits.table_size), 241 maximum: None, 242 table64: false, 243 shared: false, 244 }); 245 } 246 247 // Define our globals. 248 let mut globals = GlobalSection::new(); 249 for _ in 0..self.limits.num_globals { 250 globals.global( 251 wasm_encoder::GlobalType { 252 val_type: wasm_encoder::ValType::EXTERNREF, 253 mutable: true, 254 shared: false, 255 }, 256 &ConstExpr::ref_null(wasm_encoder::HeapType::EXTERN), 257 ); 258 } 259 260 // Add exactly one (ref.null struct) global. 261 let struct_global_idx = globals.len(); 262 globals.global( 263 wasm_encoder::GlobalType { 264 val_type: ValType::Ref(RefType { 265 nullable: true, 266 heap_type: wasm_encoder::HeapType::Abstract { 267 shared: false, 268 ty: wasm_encoder::AbstractHeapType::Struct, 269 }, 270 }), 271 mutable: true, 272 shared: false, 273 }, 274 &ConstExpr::ref_null(wasm_encoder::HeapType::Abstract { 275 shared: false, 276 ty: wasm_encoder::AbstractHeapType::Struct, 277 }), 278 ); 279 280 // Add one typed (ref <type>) global per struct type. 281 let typed_global_base = globals.len(); 282 for i in 0..struct_count { 283 let concrete = struct_type_base + i; 284 globals.global( 285 wasm_encoder::GlobalType { 286 val_type: ValType::Ref(RefType { 287 nullable: true, 288 heap_type: wasm_encoder::HeapType::Concrete(concrete), 289 }), 290 mutable: true, 291 shared: false, 292 }, 293 &ConstExpr::ref_null(wasm_encoder::HeapType::Concrete(concrete)), 294 ); 295 } 296 297 // Define the "run" function export. 298 let mut functions = FunctionSection::new(); 299 let mut exports = ExportSection::new(); 300 301 let run_defined_idx = functions.len(); 302 functions.function(1); 303 let run_func_index = imports.len() + run_defined_idx; 304 exports.export("run", ExportKind::Func, run_func_index); 305 306 // Give ourselves one scratch local that we can use in various `GcOp` 307 // implementations. 308 let mut local_decls: Vec<(u32, ValType)> = vec![(1, ValType::EXTERNREF)]; 309 310 let scratch_local = params_len; 311 let struct_local_idx = scratch_local + 1; 312 local_decls.push(( 313 1, 314 ValType::Ref(RefType { 315 nullable: true, 316 heap_type: wasm_encoder::HeapType::Abstract { 317 shared: false, 318 ty: wasm_encoder::AbstractHeapType::Struct, 319 }, 320 }), 321 )); 322 323 let typed_local_base: u32 = struct_local_idx + 1; 324 for i in 0..struct_count { 325 let concrete = struct_type_base + i; 326 local_decls.push(( 327 1, 328 ValType::Ref(RefType { 329 nullable: true, 330 heap_type: wasm_encoder::HeapType::Concrete(concrete), 331 }), 332 )); 333 } 334 335 let storage_bases = WasmEncodingBases { 336 struct_type_base, 337 typed_first_func_index, 338 struct_local_idx, 339 typed_local_base, 340 struct_global_idx, 341 typed_global_base, 342 struct_table_idx, 343 typed_table_base, 344 }; 345 346 let mut func = Function::new(local_decls); 347 func.instruction(&Instruction::Loop(wasm_encoder::BlockType::Empty)); 348 for op in &self.ops { 349 op.encode(&mut func, scratch_local, storage_bases); 350 } 351 func.instruction(&Instruction::Br(0)); 352 func.instruction(&Instruction::End); 353 func.instruction(&Instruction::End); 354 355 let mut code = CodeSection::new(); 356 code.function(&func); 357 358 module 359 .section(&types) 360 .section(&imports) 361 .section(&functions) 362 .section(&tables) 363 .section(&globals) 364 .section(&exports) 365 .section(&code); 366 367 module.finish() 368 } 369 370 /// Fixes this test case such that it becomes valid. 371 /// 372 /// This is necessary because a random mutation (e.g. removing an op in the 373 /// middle of our sequence) might have made it so that subsequent ops won't 374 /// have their expected operand types on the Wasm stack 375 /// anymore. Furthermore, because we serialize and deserialize test cases, 376 /// and libFuzzer will occasionally mutate those serialized bytes directly, 377 /// rather than use one of our custom mutations, we have no guarantee that 378 /// pre-mutation test cases are even valid! Therefore, we always call this 379 /// method before translating this "AST"-style representation into a raw 380 /// Wasm binary. fixup(&mut self)381 pub fn fixup(&mut self) { 382 self.limits.fixup(); 383 self.types.fixup(&self.limits); 384 385 let mut new_ops = Vec::with_capacity(self.ops.len()); 386 let mut stack: Vec<StackType> = Vec::new(); 387 let num_types = u32::try_from(self.types.type_defs.len()).unwrap(); 388 389 let mut operand_types = Vec::new(); 390 for op in &self.ops { 391 let Some(op) = op.fixup(&self.limits, num_types) else { 392 continue; 393 }; 394 395 debug_assert!(operand_types.is_empty()); 396 op.operand_types(&mut operand_types); 397 for ty in operand_types.drain(..) { 398 StackType::fixup(ty, &mut stack, &mut new_ops, num_types); 399 } 400 401 // Finally, emit the op itself (updates stack abstractly) 402 let mut result_types = Vec::new(); 403 StackType::emit(op, &mut stack, &mut new_ops, num_types, &mut result_types); 404 } 405 406 // Drop any remaining values on the operand stack. 407 for _ in 0..stack.len() { 408 new_ops.push(GcOp::Drop); 409 } 410 411 log::trace!("ops after fixup: {new_ops:#?}"); 412 self.ops = new_ops; 413 } 414 415 /// Attempts to remove the last opcode from the sequence. 416 /// 417 /// Returns `true` if an opcode was successfully removed, or `false` if the 418 /// list was already empty. pop(&mut self) -> bool419 pub fn pop(&mut self) -> bool { 420 self.ops.pop().is_some() 421 } 422 } 423 424 macro_rules! for_each_gc_op { 425 ( $mac:ident ) => { 426 $mac! { 427 #[operands([])] 428 #[results([ExternRef, ExternRef, ExternRef])] 429 Gc, 430 431 #[operands([])] 432 #[results([ExternRef, ExternRef, ExternRef])] 433 MakeRefs, 434 435 #[operands([Some(ExternRef), Some(ExternRef), Some(ExternRef)])] 436 #[results([])] 437 TakeRefs, 438 439 #[operands([])] 440 #[results([ExternRef])] 441 #[fixup(|limits, _num_types| { 442 // Add one to make sure that out-of-bounds table accesses are 443 // possible, but still rare. 444 elem_index = elem_index % (limits.table_size + 1); 445 })] 446 TableGet { elem_index: u32 }, 447 448 #[operands([Some(ExternRef)])] 449 #[results([])] 450 #[fixup(|limits, _num_types| { 451 // Add one to make sure that out-of-bounds table accesses are 452 // possible, but still rare. 453 elem_index = elem_index % (limits.table_size + 1); 454 })] 455 TableSet { elem_index: u32 }, 456 457 #[operands([])] 458 #[results([ExternRef])] 459 #[fixup(|limits, _num_types| { 460 global_index = global_index.checked_rem(limits.num_globals)?; 461 })] 462 GlobalGet { global_index: u32 }, 463 464 #[operands([Some(ExternRef)])] 465 #[results([])] 466 #[fixup(|limits, _num_types| { 467 global_index = global_index.checked_rem(limits.num_globals)?; 468 })] 469 GlobalSet { global_index: u32 }, 470 471 #[operands([])] 472 #[results([ExternRef])] 473 #[fixup(|limits, _num_types| { 474 local_index = local_index.checked_rem(limits.num_params)?; 475 })] 476 LocalGet { local_index: u32 }, 477 478 #[operands([Some(ExternRef)])] 479 #[results([])] 480 #[fixup(|limits, _num_types| { 481 local_index = local_index.checked_rem(limits.num_params)?; 482 })] 483 LocalSet { local_index: u32 }, 484 485 #[operands([])] 486 #[results([Struct(Some(type_index))])] 487 #[fixup(|_limits, num_types| { 488 type_index = type_index.checked_rem(num_types)?; 489 })] 490 StructNew { type_index: u32 }, 491 492 #[operands([Some(Struct(None))])] 493 #[results([])] 494 TakeStructCall, 495 496 #[operands([Some(Struct(Some(type_index)))])] 497 #[results([])] 498 #[fixup(|_limits, num_types| { 499 type_index = type_index.checked_rem(num_types)?; 500 })] 501 TakeTypedStructCall { type_index: u32 }, 502 503 #[operands([Some(Struct(None))])] 504 #[results([])] 505 StructLocalSet, 506 507 #[operands([])] 508 #[results([Struct(None)])] 509 StructLocalGet, 510 511 #[operands([Some(Struct(Some(type_index)))])] 512 #[results([])] 513 #[fixup(|_limits, num_types| { 514 type_index = type_index.checked_rem(num_types)?; 515 })] 516 TypedStructLocalSet { type_index: u32 }, 517 518 #[operands([])] 519 #[results([Struct(Some(type_index))])] 520 #[fixup(|_limits, num_types| { 521 type_index = type_index.checked_rem(num_types)?; 522 })] 523 TypedStructLocalGet { type_index: u32 }, 524 525 #[operands([Some(Struct(None))])] 526 #[results([])] 527 StructGlobalSet, 528 529 #[operands([])] 530 #[results([Struct(None)])] 531 StructGlobalGet, 532 533 #[operands([Some(Struct(Some(type_index)))])] 534 #[results([])] 535 #[fixup(|_limits, num_types| { 536 type_index = type_index.checked_rem(num_types)?; 537 })] 538 TypedStructGlobalSet { type_index: u32 }, 539 540 #[operands([])] 541 #[results([Struct(Some(type_index))])] 542 #[fixup(|_limits, num_types| { 543 type_index = type_index.checked_rem(num_types)?; 544 })] 545 TypedStructGlobalGet { type_index: u32 }, 546 547 #[operands([Some(Struct(None))])] 548 #[results([])] 549 #[fixup(|limits, _num_types| { 550 // Add one to make sure that out-of-bounds table accesses are 551 // possible, but still rare. 552 elem_index = elem_index % (limits.table_size + 1); 553 })] 554 StructTableSet { elem_index: u32 }, 555 556 #[operands([])] 557 #[results([Struct(None)])] 558 #[fixup(|limits, _num_types| { 559 // Add one to make sure that out-of-bounds table accesses are 560 // possible, but still rare. 561 elem_index = elem_index % (limits.table_size + 1); 562 })] 563 StructTableGet { elem_index: u32 }, 564 565 #[operands([Some(Struct(Some(type_index)))])] 566 #[results([])] 567 #[fixup(|limits, num_types| { 568 // Add one to make sure that out-of-bounds table accesses are 569 // possible, but still rare. 570 elem_index = elem_index % (limits.table_size + 1); 571 type_index = type_index.checked_rem(num_types)?; 572 })] 573 TypedStructTableSet { elem_index: u32, type_index: u32 }, 574 575 #[operands([])] 576 #[results([Struct(Some(type_index))])] 577 #[fixup(|limits, num_types| { 578 // Add one to make sure that out-of-bounds table accesses are 579 // possible, but still rare. 580 elem_index = elem_index % (limits.table_size + 1); 581 type_index = type_index.checked_rem(num_types)?; 582 })] 583 TypedStructTableGet { elem_index: u32, type_index: u32 }, 584 585 #[operands([None])] 586 #[results([])] 587 Drop, 588 589 #[operands([])] 590 #[results([ExternRef])] 591 NullExtern, 592 593 #[operands([])] 594 #[results([Struct(None)])] 595 NullStruct, 596 597 #[operands([])] 598 #[results([Struct(Some(type_index))])] 599 #[fixup(|_limits, num_types| { 600 type_index = type_index.checked_rem(num_types)?; 601 })] 602 NullTypedStruct { type_index: u32 }, 603 } 604 }; 605 } 606 607 macro_rules! define_gc_op_variants { 608 ( 609 $( 610 $( #[$attr:meta] )* 611 $op:ident $( { $( $field:ident : $field_ty:ty ),* } )? , 612 )* 613 ) => { 614 /// The operations that can be performed by the `gc` function. 615 #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] 616 #[allow(missing_docs, reason = "self-describing")] 617 pub enum GcOp { 618 $( 619 $op $( { $( $field : $field_ty ),* } )? , 620 )* 621 } 622 }; 623 } 624 for_each_gc_op!(define_gc_op_variants); 625 626 macro_rules! define_op_names { 627 ( 628 $( 629 $( #[$attr:meta] )* 630 $op:ident $( { $( $field:ident : $field_ty:ty ),* } )? , 631 )* 632 ) => { 633 #[cfg(test)] 634 pub(crate) const OP_NAMES: &[&str] = &[ 635 $(stringify!($op)),* 636 ]; 637 } 638 } 639 for_each_gc_op!(define_op_names); 640 641 impl GcOp { 642 #[cfg(test)] name(&self) -> &'static str643 pub(crate) fn name(&self) -> &'static str { 644 macro_rules! define_gc_op_name { 645 ( 646 $( 647 $( #[$attr:meta] )* 648 $op:ident $( { $( $field:ident : $field_ty:ty ),* } )? , 649 )* 650 ) => { 651 match self { 652 $( 653 Self::$op $( { $($field: _),* } )? => stringify!($op), 654 )* 655 } 656 }; 657 } 658 for_each_gc_op!(define_gc_op_name) 659 } 660 operand_types(&self, out: &mut Vec<Option<StackType>>)661 pub(crate) fn operand_types(&self, out: &mut Vec<Option<StackType>>) { 662 macro_rules! define_gc_op_operand_types { 663 ( 664 $( 665 #[operands($operands:expr)] 666 $( #[$attr:meta] )* 667 $op:ident $( { $( $field:ident : $field_ty:ty ),* } )? , 668 )* 669 ) => {{ 670 use StackType::*; 671 match self { 672 $( 673 Self::$op $( { $($field),* } )? => { 674 $( 675 $( 676 #[allow(unused, reason = "macro code")] 677 let $field = *$field; 678 )* 679 )? 680 let operands: [Option<StackType>; _] = $operands; 681 out.extend(operands); 682 } 683 )* 684 } 685 }}; 686 } 687 for_each_gc_op!(define_gc_op_operand_types) 688 } 689 result_types(&self, out: &mut Vec<StackType>)690 pub(crate) fn result_types(&self, out: &mut Vec<StackType>) { 691 macro_rules! define_gc_op_result_types { 692 ( 693 $( 694 #[operands($operands:expr)] 695 #[results($results:expr)] 696 $( #[$attr:meta] )* 697 $op:ident $( { $( $field:ident : $field_ty:ty ),* } )? , 698 )* 699 ) => {{ 700 use StackType::*; 701 match self { 702 $( 703 Self::$op $( { $($field),* } )? => { 704 $( 705 $( 706 #[allow(unused, reason = "macro code")] 707 let $field = *$field; 708 )* 709 )? 710 let results: [StackType; _] = $results; 711 out.extend(results); 712 } 713 )* 714 } 715 }}; 716 } 717 for_each_gc_op!(define_gc_op_result_types) 718 } 719 fixup(&self, limits: &GcOpsLimits, num_types: u32) -> Option<Self>720 pub(crate) fn fixup(&self, limits: &GcOpsLimits, num_types: u32) -> Option<Self> { 721 macro_rules! define_gc_op_fixup { 722 ( 723 $( 724 #[operands($operands:expr)] 725 #[results($results:expr)] 726 $( #[fixup(|$limits:ident, $num_types:ident| $fixup:expr)] )? 727 $op:ident $( { $( $field:ident : $field_ty:ty ),* } )? , 728 )* 729 ) => {{ 730 match self { 731 $( 732 Self::$op $( { $($field),* } )? => { 733 $( 734 $( 735 #[allow(unused_mut, reason = "macro code")] 736 let mut $field = *$field; 737 )* 738 let $limits = limits; 739 let $num_types = num_types; 740 $fixup; 741 )? 742 Some(Self::$op $( { $( $field ),* } )? ) 743 } 744 )* 745 } 746 }}; 747 } 748 for_each_gc_op!(define_gc_op_fixup) 749 } 750 generate(ctx: &mut mutatis::Context) -> mutatis::Result<GcOp>751 pub(crate) fn generate(ctx: &mut mutatis::Context) -> mutatis::Result<GcOp> { 752 macro_rules! define_gc_op_generate { 753 ( 754 $( 755 $( #[$attr:meta] )* 756 $op:ident $( { $( $field:ident : $field_ty:ty ),* } )? , 757 )* 758 ) => {{ 759 let choices: &[fn(&mut mutatis::Context) -> mutatis::Result<GcOp>] = &[ 760 $( 761 |_ctx| Ok(GcOp::$op $( { 762 $( 763 $field: { 764 let mut mutator = <$field_ty as mutatis::DefaultMutate>::DefaultMutate::default(); 765 mutatis::Generate::<$field_ty>::generate(&mut mutator, _ctx)? 766 } 767 ),* 768 } )? ), 769 )* 770 ]; 771 772 let f = *ctx.rng() 773 .choose(choices) 774 .unwrap(); 775 (f)(ctx) 776 }}; 777 } 778 for_each_gc_op!(define_gc_op_generate) 779 } 780 encode(&self, func: &mut Function, scratch_local: u32, encoding_bases: WasmEncodingBases)781 fn encode(&self, func: &mut Function, scratch_local: u32, encoding_bases: WasmEncodingBases) { 782 let gc_func_idx = 0; 783 let take_refs_func_idx = 1; 784 let make_refs_func_idx = 2; 785 let take_structref_idx = 3; 786 787 match *self { 788 Self::Gc => { 789 func.instruction(&Instruction::Call(gc_func_idx)); 790 } 791 Self::MakeRefs => { 792 func.instruction(&Instruction::Call(make_refs_func_idx)); 793 } 794 Self::TakeRefs => { 795 func.instruction(&Instruction::Call(take_refs_func_idx)); 796 } 797 Self::TableGet { elem_index: x } => { 798 func.instruction(&Instruction::I32Const(x.cast_signed())); 799 func.instruction(&Instruction::TableGet(0)); 800 } 801 Self::TableSet { elem_index: x } => { 802 func.instruction(&Instruction::LocalSet(scratch_local)); 803 func.instruction(&Instruction::I32Const(x.cast_signed())); 804 func.instruction(&Instruction::LocalGet(scratch_local)); 805 func.instruction(&Instruction::TableSet(0)); 806 } 807 Self::GlobalGet { global_index: x } => { 808 func.instruction(&Instruction::GlobalGet(x)); 809 } 810 Self::GlobalSet { global_index: x } => { 811 func.instruction(&Instruction::GlobalSet(x)); 812 } 813 Self::LocalGet { local_index: x } => { 814 func.instruction(&Instruction::LocalGet(x)); 815 } 816 Self::LocalSet { local_index: x } => { 817 func.instruction(&Instruction::LocalSet(x)); 818 } 819 Self::Drop => { 820 func.instruction(&Instruction::Drop); 821 } 822 Self::NullExtern => { 823 func.instruction(&Instruction::RefNull(wasm_encoder::HeapType::EXTERN)); 824 } 825 Self::NullStruct => { 826 func.instruction(&Instruction::RefNull(wasm_encoder::HeapType::Abstract { 827 shared: false, 828 ty: wasm_encoder::AbstractHeapType::Struct, 829 })); 830 } 831 Self::NullTypedStruct { type_index } => { 832 func.instruction(&Instruction::RefNull(wasm_encoder::HeapType::Concrete( 833 encoding_bases.struct_type_base + type_index, 834 ))); 835 } 836 Self::StructNew { type_index: x } => { 837 func.instruction(&Instruction::StructNew(encoding_bases.struct_type_base + x)); 838 } 839 Self::TakeStructCall => { 840 func.instruction(&Instruction::Call(take_structref_idx)); 841 } 842 Self::TakeTypedStructCall { type_index: x } => { 843 let f = encoding_bases.typed_first_func_index + x; 844 func.instruction(&Instruction::Call(f)); 845 } 846 Self::StructLocalGet => { 847 func.instruction(&Instruction::LocalGet(encoding_bases.struct_local_idx)); 848 } 849 Self::TypedStructLocalGet { type_index: x } => { 850 func.instruction(&Instruction::LocalGet(encoding_bases.typed_local_base + x)); 851 } 852 Self::StructLocalSet => { 853 func.instruction(&Instruction::LocalSet(encoding_bases.struct_local_idx)); 854 } 855 Self::TypedStructLocalSet { type_index: x } => { 856 func.instruction(&Instruction::LocalSet(encoding_bases.typed_local_base + x)); 857 } 858 Self::StructGlobalGet => { 859 func.instruction(&Instruction::GlobalGet(encoding_bases.struct_global_idx)); 860 } 861 Self::TypedStructGlobalGet { type_index: x } => { 862 func.instruction(&Instruction::GlobalGet( 863 encoding_bases.typed_global_base + x, 864 )); 865 } 866 Self::StructGlobalSet => { 867 func.instruction(&Instruction::GlobalSet(encoding_bases.struct_global_idx)); 868 } 869 Self::TypedStructGlobalSet { type_index: x } => { 870 func.instruction(&Instruction::GlobalSet( 871 encoding_bases.typed_global_base + x, 872 )); 873 } 874 Self::StructTableGet { elem_index } => { 875 func.instruction(&Instruction::I32Const(elem_index.cast_signed())); 876 func.instruction(&Instruction::TableGet(encoding_bases.struct_table_idx)); 877 } 878 Self::TypedStructTableGet { 879 elem_index, 880 type_index, 881 } => { 882 func.instruction(&Instruction::I32Const(elem_index.cast_signed())); 883 func.instruction(&Instruction::TableGet( 884 encoding_bases.typed_table_base + type_index, 885 )); 886 } 887 Self::StructTableSet { elem_index } => { 888 // Use struct_local_idx (anyref) to temporarily store the value before table.set 889 func.instruction(&Instruction::LocalSet(encoding_bases.struct_local_idx)); 890 func.instruction(&Instruction::I32Const(elem_index.cast_signed())); 891 func.instruction(&Instruction::LocalGet(encoding_bases.struct_local_idx)); 892 func.instruction(&Instruction::TableSet(encoding_bases.struct_table_idx)); 893 } 894 Self::TypedStructTableSet { 895 elem_index, 896 type_index, 897 } => { 898 func.instruction(&Instruction::LocalSet( 899 encoding_bases.typed_local_base + type_index, 900 )); 901 func.instruction(&Instruction::I32Const(elem_index.cast_signed())); 902 func.instruction(&Instruction::LocalGet( 903 encoding_bases.typed_local_base + type_index, 904 )); 905 func.instruction(&Instruction::TableSet( 906 encoding_bases.typed_table_base + type_index, 907 )); 908 } 909 } 910 } 911 } 912