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