Path: blob/main/crates/fuzzing/src/generators/gc_ops/ops.rs
3068 views
//! Operations for the `gc` operations.12use crate::generators::gc_ops::types::StackType;3use crate::generators::gc_ops::{4limits::GcOpsLimits,5types::{CompositeType, RecGroupId, StructType, TypeId, Types},6};7use serde::{Deserialize, Serialize};8use std::collections::BTreeMap;9use wasm_encoder::{10CodeSection, ConstExpr, EntityType, ExportKind, ExportSection, Function, FunctionSection,11GlobalSection, ImportSection, Instruction, Module, RefType, TableSection, TableType,12TypeSection, ValType,13};1415/// The base offsets and indices for various Wasm entities within16/// their index spaces in the the encoded Wasm binary.17#[derive(Clone, Copy)]18struct WasmEncodingBases {19struct_type_base: u32,20typed_first_func_index: u32,21struct_local_idx: u32,22typed_local_base: u32,23struct_global_idx: u32,24typed_global_base: u32,25struct_table_idx: u32,26typed_table_base: u32,27}2829/// A description of a Wasm module that makes a series of `externref` table30/// operations.31#[derive(Debug, Default, Serialize, Deserialize)]32pub struct GcOps {33pub(crate) limits: GcOpsLimits,34pub(crate) ops: Vec<GcOp>,35pub(crate) types: Types,36}3738impl GcOps {39/// Serialize this module into a Wasm binary.40///41/// The module requires several function imports. See this function's42/// implementation for their exact types.43///44/// The single export of the module is a function "run" that takes45/// `self.num_params` parameters of type `externref`.46///47/// The "run" function does not terminate; you should run it with limited48/// fuel. It also is not guaranteed to avoid traps: it may access49/// out-of-bounds of the table.50pub fn to_wasm_binary(&mut self) -> Vec<u8> {51self.fixup();5253let mut module = Module::new();5455// Encode the types for all functions that we are using.56let mut types = TypeSection::new();5758// 0: "gc"59types.ty().function(60vec![],61// Return a bunch of stuff from `gc` so that we exercise GCing when62// there is return pointer space allocated on the stack. This is63// especially important because the x64 backend currently64// dynamically adjusts the stack pointer for each call that uses65// return pointers rather than statically allocating space in the66// stack frame.67vec![ValType::EXTERNREF, ValType::EXTERNREF, ValType::EXTERNREF],68);6970// 1: "run"71let mut params: Vec<ValType> = Vec::with_capacity(self.limits.num_params as usize);72for _i in 0..self.limits.num_params {73params.push(ValType::EXTERNREF);74}75let params_len =76u32::try_from(params.len()).expect("params len should be within u32 range");77let results = vec![];78types.ty().function(params, results);7980// 2: `take_refs`81types.ty().function(82vec![ValType::EXTERNREF, ValType::EXTERNREF, ValType::EXTERNREF],83vec![],84);8586// 3: `make_refs`87types.ty().function(88vec![],89vec![ValType::EXTERNREF, ValType::EXTERNREF, ValType::EXTERNREF],90);9192// 4: `take_struct`93types.ty().function(94vec![ValType::Ref(RefType {95nullable: true,96heap_type: wasm_encoder::HeapType::Abstract {97shared: false,98ty: wasm_encoder::AbstractHeapType::Struct,99},100})],101vec![],102);103104let struct_type_base: u32 = types.len();105106let mut rec_groups: BTreeMap<RecGroupId, Vec<TypeId>> = self107.types108.rec_groups109.iter()110.copied()111.map(|id| (id, Vec::new()))112.collect();113114for (id, ty) in self.types.type_defs.iter() {115rec_groups.entry(ty.rec_group).or_default().push(id.clone());116}117118let encode_ty_id = |ty_id: &TypeId| -> wasm_encoder::SubType {119let def = &self.types.type_defs[ty_id];120match &def.composite_type {121CompositeType::Struct(StructType {}) => wasm_encoder::SubType {122is_final: true,123supertype_idx: None,124composite_type: wasm_encoder::CompositeType {125inner: wasm_encoder::CompositeInnerType::Struct(wasm_encoder::StructType {126fields: Box::new([]),127}),128shared: false,129describes: None,130descriptor: None,131},132},133}134};135136let mut struct_count = 0;137138for type_ids in rec_groups.values() {139let members: Vec<wasm_encoder::SubType> = type_ids.iter().map(encode_ty_id).collect();140types.ty().rec(members);141struct_count += type_ids.len() as u32;142}143144let typed_fn_type_base: u32 = struct_type_base + struct_count;145146for i in 0..struct_count {147let concrete = struct_type_base + i;148types.ty().function(149vec![ValType::Ref(RefType {150nullable: true,151heap_type: wasm_encoder::HeapType::Concrete(concrete),152})],153vec![],154);155}156157// Import the GC function.158let mut imports = ImportSection::new();159imports.import("", "gc", EntityType::Function(0));160imports.import("", "take_refs", EntityType::Function(2));161imports.import("", "make_refs", EntityType::Function(3));162imports.import("", "take_struct", EntityType::Function(4));163164// For each of our concrete struct types, define a function165// import that takes an argument of that concrete type.166let typed_first_func_index: u32 = imports.len();167168for i in 0..struct_count {169let ty_idx = typed_fn_type_base + i;170let name = format!("take_struct_{}", struct_type_base + i);171imports.import("", &name, EntityType::Function(ty_idx));172}173174// Define our table.175let mut tables = TableSection::new();176tables.table(TableType {177element_type: RefType::EXTERNREF,178minimum: u64::from(self.limits.table_size),179maximum: None,180table64: false,181shared: false,182});183184let struct_table_idx = tables.len();185tables.table(TableType {186element_type: RefType {187nullable: true,188heap_type: wasm_encoder::HeapType::Abstract {189shared: false,190ty: wasm_encoder::AbstractHeapType::Struct,191},192},193minimum: u64::from(self.limits.table_size),194maximum: None,195table64: false,196shared: false,197});198199let typed_table_base = tables.len();200for i in 0..struct_count {201let concrete = struct_type_base + i;202tables.table(TableType {203element_type: RefType {204nullable: true,205heap_type: wasm_encoder::HeapType::Concrete(concrete),206},207minimum: u64::from(self.limits.table_size),208maximum: None,209table64: false,210shared: false,211});212}213214// Define our globals.215let mut globals = GlobalSection::new();216for _ in 0..self.limits.num_globals {217globals.global(218wasm_encoder::GlobalType {219val_type: wasm_encoder::ValType::EXTERNREF,220mutable: true,221shared: false,222},223&ConstExpr::ref_null(wasm_encoder::HeapType::EXTERN),224);225}226227// Add exactly one (ref.null struct) global.228let struct_global_idx = globals.len();229globals.global(230wasm_encoder::GlobalType {231val_type: ValType::Ref(RefType {232nullable: true,233heap_type: wasm_encoder::HeapType::Abstract {234shared: false,235ty: wasm_encoder::AbstractHeapType::Struct,236},237}),238mutable: true,239shared: false,240},241&ConstExpr::ref_null(wasm_encoder::HeapType::Abstract {242shared: false,243ty: wasm_encoder::AbstractHeapType::Struct,244}),245);246247// Add one typed (ref <type>) global per struct type.248let typed_global_base = globals.len();249for i in 0..struct_count {250let concrete = struct_type_base + i;251globals.global(252wasm_encoder::GlobalType {253val_type: ValType::Ref(RefType {254nullable: true,255heap_type: wasm_encoder::HeapType::Concrete(concrete),256}),257mutable: true,258shared: false,259},260&ConstExpr::ref_null(wasm_encoder::HeapType::Concrete(concrete)),261);262}263264// Define the "run" function export.265let mut functions = FunctionSection::new();266let mut exports = ExportSection::new();267268let run_defined_idx = functions.len();269functions.function(1);270let run_func_index = imports.len() + run_defined_idx;271exports.export("run", ExportKind::Func, run_func_index);272273// Give ourselves one scratch local that we can use in various `GcOp`274// implementations.275let mut local_decls: Vec<(u32, ValType)> = vec![(1, ValType::EXTERNREF)];276277let scratch_local = params_len;278let struct_local_idx = scratch_local + 1;279local_decls.push((2801,281ValType::Ref(RefType {282nullable: true,283heap_type: wasm_encoder::HeapType::Abstract {284shared: false,285ty: wasm_encoder::AbstractHeapType::Struct,286},287}),288));289290let typed_local_base: u32 = struct_local_idx + 1;291for i in 0..struct_count {292let concrete = struct_type_base + i;293local_decls.push((2941,295ValType::Ref(RefType {296nullable: true,297heap_type: wasm_encoder::HeapType::Concrete(concrete),298}),299));300}301302let storage_bases = WasmEncodingBases {303struct_type_base,304typed_first_func_index,305struct_local_idx,306typed_local_base,307struct_global_idx,308typed_global_base,309struct_table_idx,310typed_table_base,311};312313let mut func = Function::new(local_decls);314func.instruction(&Instruction::Loop(wasm_encoder::BlockType::Empty));315for op in &self.ops {316op.encode(&mut func, scratch_local, storage_bases);317}318func.instruction(&Instruction::Br(0));319func.instruction(&Instruction::End);320func.instruction(&Instruction::End);321322let mut code = CodeSection::new();323code.function(&func);324325module326.section(&types)327.section(&imports)328.section(&functions)329.section(&tables)330.section(&globals)331.section(&exports)332.section(&code);333334module.finish()335}336337/// Fixes this test case such that it becomes valid.338///339/// This is necessary because a random mutation (e.g. removing an op in the340/// middle of our sequence) might have made it so that subsequent ops won't341/// have their expected operand types on the Wasm stack342/// anymore. Furthermore, because we serialize and deserialize test cases,343/// and libFuzzer will occasionally mutate those serialized bytes directly,344/// rather than use one of our custom mutations, we have no guarantee that345/// pre-mutation test cases are even valid! Therefore, we always call this346/// method before translating this "AST"-style representation into a raw347/// Wasm binary.348pub fn fixup(&mut self) {349self.limits.fixup();350self.types.fixup(&self.limits);351352let mut new_ops = Vec::with_capacity(self.ops.len());353let mut stack: Vec<StackType> = Vec::new();354let num_types = u32::try_from(self.types.type_defs.len()).unwrap();355356let mut operand_types = Vec::new();357for op in &self.ops {358let Some(op) = op.fixup(&self.limits, num_types) else {359continue;360};361362debug_assert!(operand_types.is_empty());363op.operand_types(&mut operand_types);364for ty in operand_types.drain(..) {365StackType::fixup(ty, &mut stack, &mut new_ops, num_types);366}367368// Finally, emit the op itself (updates stack abstractly)369let mut result_types = Vec::new();370StackType::emit(op, &mut stack, &mut new_ops, num_types, &mut result_types);371}372373// Drop any remaining values on the operand stack.374for _ in 0..stack.len() {375new_ops.push(GcOp::Drop);376}377378log::trace!("ops after fixup: {new_ops:#?}");379self.ops = new_ops;380}381382/// Attempts to remove the last opcode from the sequence.383///384/// Returns `true` if an opcode was successfully removed, or `false` if the385/// list was already empty.386pub fn pop(&mut self) -> bool {387self.ops.pop().is_some()388}389}390391macro_rules! for_each_gc_op {392( $mac:ident ) => {393$mac! {394#[operands([])]395#[results([ExternRef, ExternRef, ExternRef])]396Gc,397398#[operands([])]399#[results([ExternRef, ExternRef, ExternRef])]400MakeRefs,401402#[operands([Some(ExternRef), Some(ExternRef), Some(ExternRef)])]403#[results([])]404TakeRefs,405406#[operands([])]407#[results([ExternRef])]408#[fixup(|limits, _num_types| {409// Add one to make sure that out-of-bounds table accesses are410// possible, but still rare.411elem_index = elem_index % (limits.table_size + 1);412})]413TableGet { elem_index: u32 },414415#[operands([Some(ExternRef)])]416#[results([])]417#[fixup(|limits, _num_types| {418// Add one to make sure that out-of-bounds table accesses are419// possible, but still rare.420elem_index = elem_index % (limits.table_size + 1);421})]422TableSet { elem_index: u32 },423424#[operands([])]425#[results([ExternRef])]426#[fixup(|limits, _num_types| {427global_index = global_index.checked_rem(limits.num_globals)?;428})]429GlobalGet { global_index: u32 },430431#[operands([Some(ExternRef)])]432#[results([])]433#[fixup(|limits, _num_types| {434global_index = global_index.checked_rem(limits.num_globals)?;435})]436GlobalSet { global_index: u32 },437438#[operands([])]439#[results([ExternRef])]440#[fixup(|limits, _num_types| {441local_index = local_index.checked_rem(limits.num_params)?;442})]443LocalGet { local_index: u32 },444445#[operands([Some(ExternRef)])]446#[results([])]447#[fixup(|limits, _num_types| {448local_index = local_index.checked_rem(limits.num_params)?;449})]450LocalSet { local_index: u32 },451452#[operands([])]453#[results([Struct(Some(type_index))])]454#[fixup(|_limits, num_types| {455type_index = type_index.checked_rem(num_types)?;456})]457StructNew { type_index: u32 },458459#[operands([Some(Struct(None))])]460#[results([])]461TakeStructCall,462463#[operands([Some(Struct(Some(type_index)))])]464#[results([])]465#[fixup(|_limits, num_types| {466type_index = type_index.checked_rem(num_types)?;467})]468TakeTypedStructCall { type_index: u32 },469470#[operands([Some(Struct(None))])]471#[results([])]472StructLocalSet,473474#[operands([])]475#[results([Struct(None)])]476StructLocalGet,477478#[operands([Some(Struct(Some(type_index)))])]479#[results([])]480#[fixup(|_limits, num_types| {481type_index = type_index.checked_rem(num_types)?;482})]483TypedStructLocalSet { type_index: u32 },484485#[operands([])]486#[results([Struct(Some(type_index))])]487#[fixup(|_limits, num_types| {488type_index = type_index.checked_rem(num_types)?;489})]490TypedStructLocalGet { type_index: u32 },491492#[operands([Some(Struct(None))])]493#[results([])]494StructGlobalSet,495496#[operands([])]497#[results([Struct(None)])]498StructGlobalGet,499500#[operands([Some(Struct(Some(type_index)))])]501#[results([])]502#[fixup(|_limits, num_types| {503type_index = type_index.checked_rem(num_types)?;504})]505TypedStructGlobalSet { type_index: u32 },506507#[operands([])]508#[results([Struct(Some(type_index))])]509#[fixup(|_limits, num_types| {510type_index = type_index.checked_rem(num_types)?;511})]512TypedStructGlobalGet { type_index: u32 },513514#[operands([Some(Struct(None))])]515#[results([])]516#[fixup(|limits, _num_types| {517// Add one to make sure that out-of-bounds table accesses are518// possible, but still rare.519elem_index = elem_index % (limits.table_size + 1);520})]521StructTableSet { elem_index: u32 },522523#[operands([])]524#[results([Struct(None)])]525#[fixup(|limits, _num_types| {526// Add one to make sure that out-of-bounds table accesses are527// possible, but still rare.528elem_index = elem_index % (limits.table_size + 1);529})]530StructTableGet { elem_index: u32 },531532#[operands([Some(Struct(Some(type_index)))])]533#[results([])]534#[fixup(|limits, num_types| {535// Add one to make sure that out-of-bounds table accesses are536// possible, but still rare.537elem_index = elem_index % (limits.table_size + 1);538type_index = type_index.checked_rem(num_types)?;539})]540TypedStructTableSet { elem_index: u32, type_index: u32 },541542#[operands([])]543#[results([Struct(Some(type_index))])]544#[fixup(|limits, num_types| {545// Add one to make sure that out-of-bounds table accesses are546// possible, but still rare.547elem_index = elem_index % (limits.table_size + 1);548type_index = type_index.checked_rem(num_types)?;549})]550TypedStructTableGet { elem_index: u32, type_index: u32 },551552#[operands([None])]553#[results([])]554Drop,555556#[operands([])]557#[results([ExternRef])]558NullExtern,559560#[operands([])]561#[results([Struct(None)])]562NullStruct,563564#[operands([])]565#[results([Struct(Some(type_index))])]566#[fixup(|_limits, num_types| {567type_index = type_index.checked_rem(num_types)?;568})]569NullTypedStruct { type_index: u32 },570}571};572}573574macro_rules! define_gc_op_variants {575(576$(577$( #[$attr:meta] )*578$op:ident $( { $( $field:ident : $field_ty:ty ),* } )? ,579)*580) => {581/// The operations that can be performed by the `gc` function.582#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]583#[allow(missing_docs, reason = "self-describing")]584pub enum GcOp {585$(586$op $( { $( $field : $field_ty ),* } )? ,587)*588}589};590}591for_each_gc_op!(define_gc_op_variants);592593macro_rules! define_op_names {594(595$(596$( #[$attr:meta] )*597$op:ident $( { $( $field:ident : $field_ty:ty ),* } )? ,598)*599) => {600#[cfg(test)]601pub(crate) const OP_NAMES: &[&str] = &[602$(stringify!($op)),*603];604}605}606for_each_gc_op!(define_op_names);607608impl GcOp {609#[cfg(test)]610pub(crate) fn name(&self) -> &'static str {611macro_rules! define_gc_op_name {612(613$(614$( #[$attr:meta] )*615$op:ident $( { $( $field:ident : $field_ty:ty ),* } )? ,616)*617) => {618match self {619$(620Self::$op $( { $($field: _),* } )? => stringify!($op),621)*622}623};624}625for_each_gc_op!(define_gc_op_name)626}627628pub(crate) fn operand_types(&self, out: &mut Vec<Option<StackType>>) {629macro_rules! define_gc_op_operand_types {630(631$(632#[operands($operands:expr)]633$( #[$attr:meta] )*634$op:ident $( { $( $field:ident : $field_ty:ty ),* } )? ,635)*636) => {{637use StackType::*;638match self {639$(640Self::$op $( { $($field),* } )? => {641$(642$(643#[allow(unused, reason = "macro code")]644let $field = *$field;645)*646)?647let operands: [Option<StackType>; _] = $operands;648out.extend(operands);649}650)*651}652}};653}654for_each_gc_op!(define_gc_op_operand_types)655}656657pub(crate) fn result_types(&self, out: &mut Vec<StackType>) {658macro_rules! define_gc_op_result_types {659(660$(661#[operands($operands:expr)]662#[results($results:expr)]663$( #[$attr:meta] )*664$op:ident $( { $( $field:ident : $field_ty:ty ),* } )? ,665)*666) => {{667use StackType::*;668match self {669$(670Self::$op $( { $($field),* } )? => {671$(672$(673#[allow(unused, reason = "macro code")]674let $field = *$field;675)*676)?677let results: [StackType; _] = $results;678out.extend(results);679}680)*681}682}};683}684for_each_gc_op!(define_gc_op_result_types)685}686687pub(crate) fn fixup(&self, limits: &GcOpsLimits, num_types: u32) -> Option<Self> {688macro_rules! define_gc_op_fixup {689(690$(691#[operands($operands:expr)]692#[results($results:expr)]693$( #[fixup(|$limits:ident, $num_types:ident| $fixup:expr)] )?694$op:ident $( { $( $field:ident : $field_ty:ty ),* } )? ,695)*696) => {{697match self {698$(699Self::$op $( { $($field),* } )? => {700$(701$(702#[allow(unused_mut, reason = "macro code")]703let mut $field = *$field;704)*705let $limits = limits;706let $num_types = num_types;707$fixup;708)?709Some(Self::$op $( { $( $field ),* } )? )710}711)*712}713}};714}715for_each_gc_op!(define_gc_op_fixup)716}717718pub(crate) fn generate(ctx: &mut mutatis::Context) -> mutatis::Result<GcOp> {719macro_rules! define_gc_op_generate {720(721$(722$( #[$attr:meta] )*723$op:ident $( { $( $field:ident : $field_ty:ty ),* } )? ,724)*725) => {{726let choices: &[fn(&mut mutatis::Context) -> mutatis::Result<GcOp>] = &[727$(728|_ctx| Ok(GcOp::$op $( {729$(730$field: {731let mut mutator = <$field_ty as mutatis::DefaultMutate>::DefaultMutate::default();732mutatis::Generate::<$field_ty>::generate(&mut mutator, _ctx)?733}734),*735} )? ),736)*737];738739let f = *ctx.rng()740.choose(choices)741.unwrap();742(f)(ctx)743}};744}745for_each_gc_op!(define_gc_op_generate)746}747748fn encode(&self, func: &mut Function, scratch_local: u32, encoding_bases: WasmEncodingBases) {749let gc_func_idx = 0;750let take_refs_func_idx = 1;751let make_refs_func_idx = 2;752let take_structref_idx = 3;753754match *self {755Self::Gc => {756func.instruction(&Instruction::Call(gc_func_idx));757}758Self::MakeRefs => {759func.instruction(&Instruction::Call(make_refs_func_idx));760}761Self::TakeRefs => {762func.instruction(&Instruction::Call(take_refs_func_idx));763}764Self::TableGet { elem_index: x } => {765func.instruction(&Instruction::I32Const(x.cast_signed()));766func.instruction(&Instruction::TableGet(0));767}768Self::TableSet { elem_index: x } => {769func.instruction(&Instruction::LocalSet(scratch_local));770func.instruction(&Instruction::I32Const(x.cast_signed()));771func.instruction(&Instruction::LocalGet(scratch_local));772func.instruction(&Instruction::TableSet(0));773}774Self::GlobalGet { global_index: x } => {775func.instruction(&Instruction::GlobalGet(x));776}777Self::GlobalSet { global_index: x } => {778func.instruction(&Instruction::GlobalSet(x));779}780Self::LocalGet { local_index: x } => {781func.instruction(&Instruction::LocalGet(x));782}783Self::LocalSet { local_index: x } => {784func.instruction(&Instruction::LocalSet(x));785}786Self::Drop => {787func.instruction(&Instruction::Drop);788}789Self::NullExtern => {790func.instruction(&Instruction::RefNull(wasm_encoder::HeapType::EXTERN));791}792Self::NullStruct => {793func.instruction(&Instruction::RefNull(wasm_encoder::HeapType::Abstract {794shared: false,795ty: wasm_encoder::AbstractHeapType::Struct,796}));797}798Self::NullTypedStruct { type_index } => {799func.instruction(&Instruction::RefNull(wasm_encoder::HeapType::Concrete(800encoding_bases.struct_type_base + type_index,801)));802}803Self::StructNew { type_index: x } => {804func.instruction(&Instruction::StructNew(encoding_bases.struct_type_base + x));805}806Self::TakeStructCall => {807func.instruction(&Instruction::Call(take_structref_idx));808}809Self::TakeTypedStructCall { type_index: x } => {810let f = encoding_bases.typed_first_func_index + x;811func.instruction(&Instruction::Call(f));812}813Self::StructLocalGet => {814func.instruction(&Instruction::LocalGet(encoding_bases.struct_local_idx));815}816Self::TypedStructLocalGet { type_index: x } => {817func.instruction(&Instruction::LocalGet(encoding_bases.typed_local_base + x));818}819Self::StructLocalSet => {820func.instruction(&Instruction::LocalSet(encoding_bases.struct_local_idx));821}822Self::TypedStructLocalSet { type_index: x } => {823func.instruction(&Instruction::LocalSet(encoding_bases.typed_local_base + x));824}825Self::StructGlobalGet => {826func.instruction(&Instruction::GlobalGet(encoding_bases.struct_global_idx));827}828Self::TypedStructGlobalGet { type_index: x } => {829func.instruction(&Instruction::GlobalGet(830encoding_bases.typed_global_base + x,831));832}833Self::StructGlobalSet => {834func.instruction(&Instruction::GlobalSet(encoding_bases.struct_global_idx));835}836Self::TypedStructGlobalSet { type_index: x } => {837func.instruction(&Instruction::GlobalSet(838encoding_bases.typed_global_base + x,839));840}841Self::StructTableGet { elem_index } => {842func.instruction(&Instruction::I32Const(elem_index.cast_signed()));843func.instruction(&Instruction::TableGet(encoding_bases.struct_table_idx));844}845Self::TypedStructTableGet {846elem_index,847type_index,848} => {849func.instruction(&Instruction::I32Const(elem_index.cast_signed()));850func.instruction(&Instruction::TableGet(851encoding_bases.typed_table_base + type_index,852));853}854Self::StructTableSet { elem_index } => {855// Use struct_local_idx (anyref) to temporarily store the value before table.set856func.instruction(&Instruction::LocalSet(encoding_bases.struct_local_idx));857func.instruction(&Instruction::I32Const(elem_index.cast_signed()));858func.instruction(&Instruction::LocalGet(encoding_bases.struct_local_idx));859func.instruction(&Instruction::TableSet(encoding_bases.struct_table_idx));860}861Self::TypedStructTableSet {862elem_index,863type_index,864} => {865func.instruction(&Instruction::LocalSet(866encoding_bases.typed_local_base + type_index,867));868func.instruction(&Instruction::I32Const(elem_index.cast_signed()));869func.instruction(&Instruction::LocalGet(870encoding_bases.typed_local_base + type_index,871));872func.instruction(&Instruction::TableSet(873encoding_bases.typed_table_base + type_index,874));875}876}877}878}879880881