Skip to main content

wasmtime_fuzzing/generators/gc_ops/
ops.rs

1//! Operations for the `gc` operations.
2
3use crate::generators::gc_ops::stack::StackType;
4use crate::generators::gc_ops::{
5    limits::{GcOpsLimits, MAX_INLINE_CONSTRUCTION},
6    types::{CompositeType, EmitCtx, FieldType, RecGroupId, StructField, TypeId, Types, emit_new},
7};
8use mutatis::Generate;
9use serde::{Deserialize, Serialize};
10use std::collections::BTreeMap;
11use wasm_encoder::{
12    AbstractHeapType, BlockType, CodeSection, ConstExpr, EntityType, ExportKind, ExportSection,
13    Function, FunctionSection, GlobalSection, GlobalType, HeapType, ImportSection, Instruction,
14    Module, RefType, TableSection, TableType, TypeSection, ValType,
15};
16
17/// The abstract `struct` heap type, which `wasm_encoder` has no shorthand for.
18const STRUCT: HeapType = HeapType::Abstract {
19    shared: false,
20    ty: AbstractHeapType::Struct,
21};
22
23/// The abstract `array` heap type.
24const ARRAY: HeapType = HeapType::Abstract {
25    shared: false,
26    ty: AbstractHeapType::Array,
27};
28
29/// The abstract `eq` heap type.
30const EQ: HeapType = HeapType::Abstract {
31    shared: false,
32    ty: AbstractHeapType::Eq,
33};
34
35/// `structref`, i.e. `(ref null struct)`.
36const STRUCTREF: RefType = RefType {
37    nullable: true,
38    heap_type: STRUCT,
39};
40
41/// `(ref null $index)`.
42fn concrete(index: u32) -> RefType {
43    RefType {
44        nullable: true,
45        heap_type: HeapType::Concrete(index),
46    }
47}
48
49/// Pick a same-kind concrete type index for `raw`, or `None` when there are no
50/// types of that kind (so the caller drops the op).
51fn pick_type_index(indices: &[u32], raw: u32) -> Option<u32> {
52    if indices.is_empty() {
53        None
54    } else {
55        Some(indices[usize::try_from(raw).unwrap() % indices.len()])
56    }
57}
58
59/// Returns the element field if the indexed type is an array
60fn array_element<'a>(
61    types: &'a Types,
62    encoding_order: &[TypeId],
63    type_index: u32,
64) -> Option<&'a StructField> {
65    encoding_order
66        .get(usize::try_from(type_index).unwrap())
67        .and_then(|tid| types.type_defs.get(tid))
68        .and_then(|def| match &def.composite_type {
69            CompositeType::Array(at) => Some(&at.element),
70            CompositeType::Struct(_) => None,
71        })
72}
73
74/// The fields of the struct type at `type_index`, or `None` if that index is
75/// out of range or names an array type.
76fn struct_fields<'a>(
77    types: &'a Types,
78    encoding_order: &[TypeId],
79    type_index: u32,
80) -> Option<&'a [StructField]> {
81    encoding_order
82        .get(usize::try_from(type_index).unwrap())
83        .and_then(|tid| types.type_defs.get(tid))
84        .and_then(|def| match &def.composite_type {
85            CompositeType::Struct(st) => Some(st.fields.as_slice()),
86            CompositeType::Array(_) => None,
87        })
88}
89
90/// A table of `size` nullable references with no maximum.
91fn nullable_table(element_type: RefType, size: u32) -> TableType {
92    TableType {
93        element_type,
94        minimum: u64::from(size),
95        maximum: None,
96        table64: false,
97        shared: false,
98    }
99}
100
101/// Append a mutable null-initialized `(ref null heap_type)` global and return its index.
102fn null_ref_global(globals: &mut GlobalSection, heap_type: HeapType) -> u32 {
103    let index = globals.len();
104    globals.global(
105        GlobalType {
106            val_type: ValType::Ref(RefType {
107                nullable: true,
108                heap_type,
109            }),
110            mutable: true,
111            shared: false,
112        },
113        &ConstExpr::ref_null(heap_type),
114    );
115    index
116}
117
118/// `table.get` of a constant index.
119fn table_get(func: &mut Function, elem_index: u32, table: u32) {
120    func.instruction(&Instruction::I32Const(elem_index.cast_signed()));
121    func.instruction(&Instruction::TableGet(table));
122}
123
124/// `table.set` of a constant index with the value on top of the stack, parked
125/// in `tmp` (a local of the table's element type) while the index is pushed.
126fn table_set_via(func: &mut Function, tmp: u32, elem_index: u32, table: u32) {
127    func.instruction(&Instruction::LocalSet(tmp));
128    func.instruction(&Instruction::I32Const(elem_index.cast_signed()));
129    func.instruction(&Instruction::LocalGet(tmp));
130    func.instruction(&Instruction::TableSet(table));
131}
132
133/// Pop the reference on top of the stack into `tmp` and run `body` only if it is
134/// non-null, so `body` reads it back with `local.get tmp`.
135fn if_non_null(func: &mut Function, tmp: u32, body: impl FnOnce(&mut Function)) {
136    func.instruction(&Instruction::LocalTee(tmp));
137    func.instruction(&Instruction::RefIsNull);
138    func.instruction(&Instruction::If(BlockType::Empty));
139    func.instruction(&Instruction::Else);
140    body(func);
141    func.instruction(&Instruction::End);
142}
143
144/// The `struct.get` variant for a field: `_s`/`_u` if packed, plain otherwise.
145fn struct_get_instruction(
146    struct_type_index: u32,
147    field_index: u32,
148    field_type: FieldType,
149    unsigned: bool,
150) -> Instruction<'static> {
151    match (field_type.is_packed(), unsigned) {
152        (false, _) => Instruction::StructGet {
153            struct_type_index,
154            field_index,
155        },
156        (true, true) => Instruction::StructGetU {
157            struct_type_index,
158            field_index,
159        },
160        (true, false) => Instruction::StructGetS {
161            struct_type_index,
162            field_index,
163        },
164    }
165}
166
167/// The `array.get` variant for an element type: `_s`/`_u` if packed, plain otherwise.
168fn array_get_instruction(
169    array_type_index: u32,
170    element_type: FieldType,
171    unsigned: bool,
172) -> Instruction<'static> {
173    match (element_type.is_packed(), unsigned) {
174        (false, _) => Instruction::ArrayGet(array_type_index),
175        (true, true) => Instruction::ArrayGetU(array_type_index),
176        (true, false) => Instruction::ArrayGetS(array_type_index),
177    }
178}
179
180/// Indices of the function types of the host imports and `run`.
181struct HostTypes {
182    /// Type index of `gc`.
183    gc: u32,
184    /// Type index of `run`.
185    run: u32,
186    /// Type index of `take_refs`.
187    take_refs: u32,
188    /// Type index of `make_refs`.
189    make_refs: u32,
190    /// Type index of `take_struct`.
191    take_struct: u32,
192    /// Type index of `take_eq`.
193    take_eq: u32,
194    /// Type index of `take_i31`.
195    take_i31: u32,
196    /// Type index of `take_array`.
197    take_array: u32,
198}
199
200/// Function indices of the host imports.
201#[derive(Clone, Copy)]
202struct HostFuncs {
203    /// Function index of `gc`.
204    gc: u32,
205    /// Function index of `take_refs`.
206    take_refs: u32,
207    /// Function index of `make_refs`.
208    make_refs: u32,
209    /// Function index of `take_struct`.
210    take_struct: u32,
211    /// Function index of `take_eq`.
212    take_eq: u32,
213    /// Function index of `take_i31`.
214    take_i31: u32,
215    /// Function index of `take_array`.
216    take_array: u32,
217    /// Bank of `take_struct_N` and `take_array_N` imports, one per concrete type.
218    typed_base: u32,
219}
220
221impl HostFuncs {
222    /// Function index of the `take_*` import for the concrete type at `dense`.
223    fn typed(&self, dense: u32) -> u32 {
224        self.typed_base + dense
225    }
226}
227
228/// One slot per abstract reference type plus a bank of typed slots. A bank is a
229/// run of consecutive slots, one per concrete type, at `base + dense`.
230#[derive(Clone, Copy)]
231struct RootBanks {
232    /// The `structref` slot.
233    structref: u32,
234    /// The `eqref` slot.
235    eqref: u32,
236    /// The `i31ref` slot.
237    i31ref: u32,
238    /// The `arrayref` slot.
239    arrayref: u32,
240    /// First slot of the bank of typed slots.
241    typed_base: u32,
242}
243
244impl RootBanks {
245    /// Slot of the concrete type at `dense`.
246    fn typed(&self, dense: u32) -> u32 {
247        self.typed_base + dense
248    }
249}
250
251/// Locals of `run` after its `externref` parameters; see `RootBanks` for banks.
252#[derive(Clone, Copy)]
253struct LocalBanks {
254    /// Temporary for `table.set` on the `externref` table.
255    extern_scratch: u32,
256    /// The `structref` root local.
257    structref: u32,
258    /// The `eqref` root local.
259    eqref: u32,
260    /// The `i31ref` root local.
261    i31ref: u32,
262    /// The `arrayref` root local.
263    arrayref: u32,
264    /// Typed roots, also the temporaries of null-guarded ops on that type.
265    typed_base: u32,
266    /// Second operand for ops on two values of one type (`array.copy`).
267    typed2_base: u32,
268    /// One shared prototype per type, chosen by `Types::prototype_types`.
269    proto_base: u32,
270}
271
272impl LocalBanks {
273    /// Typed root local of the concrete type at `dense`.
274    fn typed(&self, dense: u32) -> u32 {
275        self.typed_base + dense
276    }
277
278    /// Second operand local of the concrete type at `dense`.
279    fn typed2(&self, dense: u32) -> u32 {
280        self.typed2_base + dense
281    }
282
283    /// Prototype local of the concrete type at `dense`.
284    fn proto(&self, dense: u32) -> u32 {
285        self.proto_base + dense
286    }
287}
288
289/// Where everything lives in the encoded module. `dense` arguments are positions
290/// in the encoding order of the concrete types, which is what `type_index` holds.
291#[derive(Clone, Copy)]
292pub(crate) struct WasmEncodingBases {
293    /// Function indices of the host imports.
294    funcs: HostFuncs,
295    /// Wasm index of the first concrete type.
296    struct_type_base: u32,
297    /// Locals of `run`.
298    locals: LocalBanks,
299    /// Globals of the module.
300    globals: RootBanks,
301    /// Tables of the module.
302    tables: RootBanks,
303    /// Length of every array `ArrayNew` / `ArrayNewDefault` creates.
304    array_length: u32,
305}
306
307impl WasmEncodingBases {
308    /// Wasm type index of the concrete type at `dense`.
309    fn wasm_type(&self, dense: u32) -> u32 {
310        self.struct_type_base + dense
311    }
312
313    /// The slots of one root kind.
314    fn root_slots(&self, kind: RefKind) -> RootSlots {
315        let (l, g, t) = (&self.locals, &self.globals, &self.tables);
316        match kind {
317            RefKind::Extern { slot } => RootSlots {
318                local: slot,
319                global: slot,
320                table: 0,
321                tmp: l.extern_scratch,
322            },
323            RefKind::Struct => RootSlots {
324                local: l.structref,
325                global: g.structref,
326                table: t.structref,
327                tmp: l.structref,
328            },
329            RefKind::Eq => RootSlots {
330                local: l.eqref,
331                global: g.eqref,
332                table: t.eqref,
333                tmp: l.eqref,
334            },
335            RefKind::I31 => RootSlots {
336                local: l.i31ref,
337                global: g.i31ref,
338                table: t.i31ref,
339                tmp: l.i31ref,
340            },
341            RefKind::Array => RootSlots {
342                local: l.arrayref,
343                global: g.arrayref,
344                table: t.arrayref,
345                tmp: l.arrayref,
346            },
347            RefKind::Typed(d) => RootSlots {
348                local: l.typed(d),
349                global: g.typed(d),
350                table: t.typed(d),
351                tmp: l.typed(d),
352            },
353        }
354    }
355}
356
357/// The kinds of reference the module keeps roots for, with `Typed` holding a dense type index.
358#[derive(Clone, Copy, Debug)]
359enum RefKind {
360    /// An `externref`, in the parameter or global `slot` (unused for a table access).
361    Extern { slot: u32 },
362    /// A `structref`.
363    Struct,
364    /// An `eqref`.
365    Eq,
366    /// An `i31ref`.
367    I31,
368    /// An `arrayref`.
369    Array,
370    /// A reference to the concrete type at this dense index.
371    Typed(u32),
372}
373
374/// Where a root access reads or writes.
375#[derive(Clone, Copy, Debug)]
376enum Storage {
377    /// A local of `run`.
378    Local,
379    /// A global.
380    Global,
381    /// The element at this index of a table.
382    Table(u32),
383}
384
385/// Whether a root access reads or writes.
386#[derive(Clone, Copy, Debug)]
387enum Dir {
388    /// Read the root.
389    Get,
390    /// Write the root.
391    Set,
392}
393
394/// The local, global, table and `table.set` temporary of one root kind.
395#[derive(Clone, Copy)]
396struct RootSlots {
397    /// The local.
398    local: u32,
399    /// The global.
400    global: u32,
401    /// The table.
402    table: u32,
403    /// The local `table.set` parks its value in.
404    tmp: u32,
405}
406
407/// Local declarations of a function, handing out indices in declaration order.
408struct LocalDecls {
409    /// The declarations, one entry per local.
410    decls: Vec<(u32, ValType)>,
411    /// Index of the next local to declare.
412    next: u32,
413}
414
415impl LocalDecls {
416    /// No locals yet, after `num_params` parameters.
417    fn new(num_params: u32) -> Self {
418        Self {
419            decls: Vec::new(),
420            next: num_params,
421        }
422    }
423
424    /// Declare one local of type `ty` and return its index.
425    fn declare(&mut self, ty: ValType) -> u32 {
426        let index = self.next;
427        self.next += 1;
428        self.decls.push((1, ty));
429        index
430    }
431
432    /// Declare a bank of `count` locals and return its base index.
433    fn declare_bank(&mut self, count: u32, ty: impl Fn(u32) -> ValType) -> u32 {
434        let base = self.next;
435        for i in 0..count {
436            self.declare(ty(i));
437        }
438        base
439    }
440}
441
442/// The function types of the host imports and of `run`, which takes `num_params` `externref`s.
443fn host_function_types(types: &mut TypeSection, num_params: u32) -> HostTypes {
444    let three_refs = vec![ValType::EXTERNREF, ValType::EXTERNREF, ValType::EXTERNREF];
445
446    // `gc` returns a bunch of stuff so that we exercise GCing when there is
447    // return pointer space allocated on the stack. This is especially
448    // important because the x64 backend currently dynamically adjusts the
449    // stack pointer for each call that uses return pointers rather than
450    // statically allocating space in the stack frame.
451    let gc = types.len();
452    types.ty().function(vec![], three_refs.clone());
453
454    let run = types.len();
455    types.ty().function(
456        vec![ValType::EXTERNREF; usize::try_from(num_params).unwrap()],
457        vec![],
458    );
459
460    let take_refs = types.len();
461    types.ty().function(three_refs.clone(), vec![]);
462
463    let make_refs = types.len();
464    types.ty().function(vec![], three_refs);
465
466    let take_struct = types.len();
467    types.ty().function(vec![ValType::Ref(STRUCTREF)], vec![]);
468
469    let take_eq = types.len();
470    types
471        .ty()
472        .function(vec![ValType::Ref(RefType::EQREF)], vec![]);
473
474    // `take_i31` also receives the guest's inline `i31.get_s` / `i31.get_u`
475    // results, so the host can check its view of the i31 against them.
476    let take_i31 = types.len();
477    types.ty().function(
478        vec![ValType::Ref(RefType::I31REF), ValType::I32, ValType::I32],
479        vec![],
480    );
481
482    let take_array = types.len();
483    types
484        .ty()
485        .function(vec![ValType::Ref(RefType::ARRAYREF)], vec![]);
486
487    HostTypes {
488        gc,
489        run,
490        take_refs,
491        make_refs,
492        take_struct,
493        take_eq,
494        take_i31,
495        take_array,
496    }
497}
498
499/// Add one `(func (param (ref null $t)))` type per concrete type and return the first index.
500fn typed_take_types(types: &mut TypeSection, struct_type_base: u32, concrete_count: u32) -> u32 {
501    // Not `types.len()`: a rec group is one section entry but `concrete_count` type indices.
502    let base = struct_type_base + concrete_count;
503    for i in 0..concrete_count {
504        types
505            .ty()
506            .function(vec![ValType::Ref(concrete(struct_type_base + i))], vec![]);
507    }
508    base
509}
510
511/// Tables of `table_size` elements, the `externref` one first, then one per abstract kind and one per concrete type.
512fn encode_tables(
513    table_size: u32,
514    struct_type_base: u32,
515    concrete_count: u32,
516) -> (TableSection, RootBanks) {
517    let mut tables = TableSection::new();
518    tables.table(nullable_table(RefType::EXTERNREF, table_size));
519
520    let structref = tables.len();
521    tables.table(nullable_table(STRUCTREF, table_size));
522
523    let eqref = tables.len();
524    tables.table(nullable_table(RefType::EQREF, table_size));
525
526    let i31ref = tables.len();
527    tables.table(nullable_table(RefType::I31REF, table_size));
528
529    let arrayref = tables.len();
530    tables.table(nullable_table(RefType::ARRAYREF, table_size));
531
532    let typed_base = tables.len();
533    for i in 0..concrete_count {
534        tables.table(nullable_table(concrete(struct_type_base + i), table_size));
535    }
536
537    let banks = RootBanks {
538        structref,
539        eqref,
540        i31ref,
541        arrayref,
542        typed_base,
543    };
544    (tables, banks)
545}
546
547/// Null-initialized globals, `num_globals` `externref`s first, then one per abstract kind and one per concrete type.
548fn encode_globals(
549    num_globals: u32,
550    struct_type_base: u32,
551    concrete_count: u32,
552) -> (GlobalSection, RootBanks) {
553    let mut globals = GlobalSection::new();
554    for _ in 0..num_globals {
555        null_ref_global(&mut globals, HeapType::EXTERN);
556    }
557    let structref = null_ref_global(&mut globals, STRUCT);
558    let eqref = null_ref_global(&mut globals, EQ);
559    let i31ref = null_ref_global(&mut globals, HeapType::I31);
560    let arrayref = null_ref_global(&mut globals, ARRAY);
561    let typed_base = globals.len();
562    for i in 0..concrete_count {
563        null_ref_global(&mut globals, HeapType::Concrete(struct_type_base + i));
564    }
565
566    let banks = RootBanks {
567        structref,
568        eqref,
569        i31ref,
570        arrayref,
571        typed_base,
572    };
573    (globals, banks)
574}
575
576/// The locals of `run`; see `LocalBanks` for what each one is.
577fn declare_locals(
578    num_params: u32,
579    struct_type_base: u32,
580    concrete_count: u32,
581) -> (LocalDecls, LocalBanks) {
582    let mut locals = LocalDecls::new(num_params);
583    let extern_scratch = locals.declare(ValType::EXTERNREF);
584    let structref = locals.declare(ValType::Ref(STRUCTREF));
585    let eqref = locals.declare(ValType::Ref(RefType::EQREF));
586    let i31ref = locals.declare(ValType::Ref(RefType::I31REF));
587    let arrayref = locals.declare(ValType::Ref(RefType::ARRAYREF));
588
589    let typed = |i| ValType::Ref(concrete(struct_type_base + i));
590    let typed_base = locals.declare_bank(concrete_count, typed);
591    let typed2_base = locals.declare_bank(concrete_count, typed);
592    let proto_base = locals.declare_bank(concrete_count, typed);
593
594    let banks = LocalBanks {
595        extern_scratch,
596        structref,
597        eqref,
598        i31ref,
599        arrayref,
600        typed_base,
601        typed2_base,
602        proto_base,
603    };
604    (locals, banks)
605}
606
607/// A description of a Wasm module that performs a series of GC operations on
608/// `externref`s, `i31`s, and struct and array objects of its own types.
609#[derive(Clone, Debug, Default, Serialize, Deserialize)]
610pub struct GcOps {
611    /// Limits controlling the structure of the module.
612    pub(crate) limits: GcOpsLimits,
613    /// The operations `run` performs, in order.
614    pub(crate) ops: Vec<GcOp>,
615    /// The struct and array types of the module.
616    pub(crate) types: Types,
617}
618
619impl GcOps {
620    /// Serialize this module into a Wasm binary.
621    ///
622    /// The module requires several function imports. See this function's
623    /// implementation for their exact types.
624    ///
625    /// The single export of the module is a function "run" that takes
626    /// `self.num_params` parameters of type `externref`.
627    ///
628    /// The "run" function does not terminate; you should run it with limited
629    /// fuel. It also is not guaranteed to avoid traps: it may access
630    /// out-of-bounds of the table.
631    pub fn to_wasm_binary(&mut self) -> Vec<u8> {
632        let mut encoding_order_grouped = Vec::with_capacity(self.types.rec_groups.len());
633        self.fixup(&mut encoding_order_grouped);
634
635        // Flat encoding order (dense index -> TypeId), for naming the typed
636        // host imports and for field lookups during encoding.
637        let encoding_order: Vec<TypeId> = encoding_order_grouped
638            .iter()
639            .flat_map(|(_, members)| members.iter().copied())
640            .collect();
641
642        let mut types = TypeSection::new();
643        let host_types = host_function_types(&mut types, self.limits.num_params);
644        let struct_type_base = types.len();
645        let type_ids_to_index =
646            self.encode_concrete_types(&mut types, &encoding_order_grouped, struct_type_base);
647        let concrete_count = u32::try_from(type_ids_to_index.len()).unwrap();
648        let typed_fn_type_base = typed_take_types(&mut types, struct_type_base, concrete_count);
649
650        let (imports, funcs) = self.encode_imports(
651            &host_types,
652            typed_fn_type_base,
653            struct_type_base,
654            &encoding_order,
655        );
656        let (tables, table_banks) =
657            encode_tables(self.limits.table_size, struct_type_base, concrete_count);
658        let (globals, global_banks) =
659            encode_globals(self.limits.num_globals, struct_type_base, concrete_count);
660        let (local_decls, local_banks) =
661            declare_locals(self.limits.num_params, struct_type_base, concrete_count);
662
663        // `run` is the first (and only) defined function, right after the imports.
664        let mut functions = FunctionSection::new();
665        let mut exports = ExportSection::new();
666        functions.function(host_types.run);
667        exports.export("run", ExportKind::Func, imports.len());
668
669        let bases = WasmEncodingBases {
670            funcs,
671            struct_type_base,
672            locals: local_banks,
673            globals: global_banks,
674            tables: table_banks,
675            array_length: self.limits.array_length,
676        };
677        let func = self.encode_run_body(local_decls, bases, &type_ids_to_index, &encoding_order);
678        let mut code = CodeSection::new();
679        code.function(&func);
680
681        let mut module = Module::new();
682        module
683            .section(&types)
684            .section(&imports)
685            .section(&functions)
686            .section(&tables)
687            .section(&globals)
688            .section(&exports)
689            .section(&code);
690
691        module.finish()
692    }
693
694    /// Emit every rec group in encoding order and return the Wasm type index of each type.
695    fn encode_concrete_types(
696        &self,
697        types: &mut TypeSection,
698        encoding_order_grouped: &[(RecGroupId, Vec<TypeId>)],
699        struct_type_base: u32,
700    ) -> BTreeMap<TypeId, u32> {
701        // Build the type-id-to-wasm-index map from the pre-computed
702        // encoding order (rec groups in topo order, members sorted by
703        // supertype-first within each group).
704        let mut type_ids_to_index: BTreeMap<TypeId, u32> = BTreeMap::new();
705        let mut next_idx = struct_type_base;
706        for (_, members) in encoding_order_grouped {
707            for &tid in members {
708                type_ids_to_index.insert(tid, next_idx);
709                next_idx += 1;
710            }
711        }
712
713        let encode_ty_id = |ty_id: &TypeId| -> wasm_encoder::SubType {
714            let def = &self.types.type_defs[ty_id];
715            let inner = match &def.composite_type {
716                CompositeType::Struct(st) => {
717                    let fields: Box<[wasm_encoder::FieldType]> = st
718                        .fields
719                        .iter()
720                        .map(|f| wasm_encoder::FieldType {
721                            element_type: f.field_type.to_storage_type(&type_ids_to_index),
722                            mutable: f.mutable,
723                        })
724                        .collect();
725                    wasm_encoder::CompositeInnerType::Struct(wasm_encoder::StructType { fields })
726                }
727                CompositeType::Array(at) => {
728                    let element = wasm_encoder::FieldType {
729                        element_type: at.element.field_type.to_storage_type(&type_ids_to_index),
730                        mutable: at.element.mutable,
731                    };
732                    wasm_encoder::CompositeInnerType::Array(wasm_encoder::ArrayType(element))
733                }
734            };
735            wasm_encoder::SubType {
736                is_final: def.is_final,
737                supertype_idxs: def
738                    .supertype
739                    .map(|st| type_ids_to_index[&st])
740                    .into_iter()
741                    .collect(),
742                composite_type: wasm_encoder::CompositeType {
743                    inner,
744                    shared: false,
745                    describes: None,
746                    descriptor: None,
747                },
748            }
749        };
750
751        for (_, group_members) in encoding_order_grouped {
752            let members: Vec<wasm_encoder::SubType> =
753                group_members.iter().map(encode_ty_id).collect();
754            types.ty().rec(members);
755        }
756
757        type_ids_to_index
758    }
759
760    /// The host imports, the fixed ones first and then one `take_*` per concrete type.
761    fn encode_imports(
762        &self,
763        host_types: &HostTypes,
764        typed_fn_type_base: u32,
765        struct_type_base: u32,
766        encoding_order: &[TypeId],
767    ) -> (ImportSection, HostFuncs) {
768        let mut imports = ImportSection::new();
769        let mut import_func = |name: &str, type_idx: u32| -> u32 {
770            let index = imports.len();
771            imports.import("", name, EntityType::Function(type_idx));
772            index
773        };
774        let gc = import_func("gc", host_types.gc);
775        let take_refs = import_func("take_refs", host_types.take_refs);
776        let make_refs = import_func("make_refs", host_types.make_refs);
777        let take_struct = import_func("take_struct", host_types.take_struct);
778        let take_eq = import_func("take_eq", host_types.take_eq);
779        let take_i31 = import_func("take_i31", host_types.take_i31);
780        let take_array = import_func("take_array", host_types.take_array);
781
782        // The import name records the kind so the host can define it appropriately.
783        let typed_base = imports.len();
784        for (i, tid) in (0u32..).zip(encoding_order) {
785            let wasm_idx = struct_type_base + i;
786            let is_array = self
787                .types
788                .type_defs
789                .get(tid)
790                .map(|def| def.composite_type.is_array())
791                .unwrap_or(false);
792            let name = if is_array {
793                format!("take_array_{wasm_idx}")
794            } else {
795                format!("take_struct_{wasm_idx}")
796            };
797            imports.import("", &name, EntityType::Function(typed_fn_type_base + i));
798        }
799
800        let funcs = HostFuncs {
801            gc,
802            take_refs,
803            make_refs,
804            take_struct,
805            take_eq,
806            take_i31,
807            take_array,
808            typed_base,
809        };
810        (imports, funcs)
811    }
812
813    /// The body of `run`, an endless loop that refills the prototypes and then runs every op.
814    fn encode_run_body(
815        &self,
816        locals: LocalDecls,
817        bases: WasmEncodingBases,
818        type_ids_to_index: &BTreeMap<TypeId, u32>,
819        encoding_order: &[TypeId],
820    ) -> Function {
821        let mut inhabitable = BTreeMap::new();
822        self.types.inhabitable(&mut inhabitable);
823        let struct_ref_target = self.types.least_rank_inhabitable_struct(&inhabitable);
824
825        // Map each prototyped type to the local holding its instance. A type
826        // absent from this map is cheap enough to rebuild at every use.
827        let proto_order = self
828            .types
829            .prototype_types(&inhabitable, MAX_INLINE_CONSTRUCTION);
830        let protos: BTreeMap<TypeId, u32> = proto_order
831            .iter()
832            .map(|tid| {
833                let dense = type_ids_to_index[tid] - bases.struct_type_base;
834                (*tid, bases.locals.proto(dense))
835            })
836            .collect();
837
838        let ctx = EmitCtx {
839            types: &self.types,
840            struct_ref_target,
841            protos: &protos,
842            type_ids_to_index,
843            bases,
844            encoding_order,
845        };
846
847        let mut func = Function::new(locals.decls);
848        func.instruction(&Instruction::Loop(BlockType::Empty));
849
850        // Refill the prototypes at the top of every iteration, so the ops below
851        // never read a null local and each iteration allocates a fresh set of
852        // objects for the collector to find and reclaim. `proto_order` is in
853        // rank order, so a prototype's referents are already built.
854        for tid in &proto_order {
855            emit_new(*tid, &mut func, ctx);
856            func.instruction(&Instruction::LocalSet(protos[tid]));
857        }
858        for op in &self.ops {
859            op.encode(&mut func, ctx);
860        }
861        func.instruction(&Instruction::Br(0));
862        func.instruction(&Instruction::End);
863        func.instruction(&Instruction::End);
864        func
865    }
866
867    /// Fixes this test case such that it becomes valid.
868    ///
869    /// This is necessary because a random mutation (e.g. removing an op in the
870    /// middle of our sequence) might have made it so that subsequent ops won't
871    /// have their expected operand types on the Wasm stack
872    /// anymore. Furthermore, because we serialize and deserialize test cases,
873    /// and libFuzzer will occasionally mutate those serialized bytes directly,
874    /// rather than use one of our custom mutations, we have no guarantee that
875    /// pre-mutation test cases are even valid! Therefore, we always call this
876    /// method before translating this "AST"-style representation into a raw
877    /// Wasm binary.
878    pub fn fixup(&mut self, encoding_order_grouped: &mut Vec<(RecGroupId, Vec<TypeId>)>) {
879        self.limits.fixup_limits();
880        self.types.fixup_types(&self.limits, encoding_order_grouped);
881        let encoding_order: Vec<TypeId> = encoding_order_grouped
882            .iter()
883            .flat_map(|(_, members)| members.iter().copied())
884            .collect();
885
886        let mut new_ops = Vec::with_capacity(self.ops.len());
887        let mut stack: Vec<StackType> = Vec::new();
888        let num_types = u32::try_from(self.types.type_defs.len()).unwrap();
889
890        // Concrete encoding indices split by kind, so typed ops can be remapped
891        // onto a same-kind type (struct ops never point at an array, etc.).
892        let mut struct_type_indices = Vec::new();
893        let mut array_type_indices = Vec::new();
894        for (i, tid) in encoding_order.iter().enumerate() {
895            let i = u32::try_from(i).unwrap();
896            match self.types.type_defs.get(tid) {
897                Some(def) if def.composite_type.is_array() => array_type_indices.push(i),
898                Some(_) => struct_type_indices.push(i),
899                None => {}
900            }
901        }
902
903        let mut operand_types = Vec::new();
904        for op in &self.ops {
905            let Some(op) = op.fixup_immediates(
906                &self.limits,
907                num_types,
908                &struct_type_indices,
909                &array_type_indices,
910            ) else {
911                continue;
912            };
913            let op = StackType::fixup_cast(op, &self.types, &encoding_order);
914
915            debug_assert!(operand_types.is_empty());
916            op.operand_types(&mut operand_types);
917            for ty in operand_types.drain(..) {
918                StackType::fixup_operand(
919                    ty,
920                    &mut stack,
921                    &mut new_ops,
922                    num_types,
923                    &self.types,
924                    &encoding_order,
925                );
926            }
927
928            // Finally, emit the op itself (updates stack abstractly)
929            let mut result_types = Vec::new();
930            StackType::emit(op, &mut stack, &mut new_ops, num_types, &mut result_types);
931        }
932
933        // Drop any remaining values on the operand stack.
934        for _ in 0..stack.len() {
935            new_ops.push(GcOp::Drop);
936        }
937
938        log::trace!("ops after fixup: {new_ops:#?}");
939        self.ops = new_ops;
940    }
941
942    /// Attempts to remove the last opcode from the sequence.
943    ///
944    /// Returns `true` if an opcode was successfully removed, or `false` if the
945    /// list was already empty.
946    pub fn pop(&mut self) -> bool {
947        self.ops.pop().is_some()
948    }
949}
950
951macro_rules! for_each_gc_op {
952    ( $mac:ident ) => {
953        $mac! {
954            #[operands([])]
955            #[results([ExternRef, ExternRef, ExternRef])]
956            Gc,
957
958            #[operands([])]
959            #[results([ExternRef, ExternRef, ExternRef])]
960            MakeRefs,
961
962            #[operands([Some(ExternRef), Some(ExternRef), Some(ExternRef)])]
963            #[results([])]
964            TakeRefs,
965
966            #[operands([])]
967            #[results([ExternRef])]
968            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
969                // Add one to make sure that out-of-bounds table accesses are
970                // possible, but still rare.
971                elem_index = elem_index % (limits.table_size + 1);
972            })]
973            TableGet { elem_index: u32 },
974
975            #[operands([Some(ExternRef)])]
976            #[results([])]
977            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
978                // Add one to make sure that out-of-bounds table accesses are
979                // possible, but still rare.
980                elem_index = elem_index % (limits.table_size + 1);
981            })]
982            TableSet { elem_index: u32 },
983
984            #[operands([])]
985            #[results([ExternRef])]
986            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
987                global_index = global_index.checked_rem(limits.num_globals)?;
988            })]
989            GlobalGet { global_index:  u32 },
990
991            #[operands([Some(ExternRef)])]
992            #[results([])]
993            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
994                global_index = global_index.checked_rem(limits.num_globals)?;
995            })]
996            GlobalSet { global_index: u32 },
997
998            #[operands([])]
999            #[results([ExternRef])]
1000            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1001                local_index = local_index.checked_rem(limits.num_params)?;
1002            })]
1003            LocalGet { local_index: u32 },
1004
1005            #[operands([Some(ExternRef)])]
1006            #[results([])]
1007            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1008                local_index = local_index.checked_rem(limits.num_params)?;
1009            })]
1010            LocalSet { local_index: u32 },
1011
1012            #[operands([])]
1013            #[results([Struct(Some(type_index))])]
1014            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1015                type_index = pick_type_index(struct_type_indices, type_index)?;
1016            })]
1017            StructNew { type_index: u32 },
1018
1019            #[operands([])]
1020            #[results([Struct(Some(type_index))])]
1021            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1022                type_index = pick_type_index(struct_type_indices, type_index)?;
1023            })]
1024            StructNewDefault { type_index: u32 },
1025
1026            #[operands([Some(Struct(None))])]
1027            #[results([])]
1028            TakeStructCall,
1029
1030            #[operands([Some(Struct(Some(type_index)))])]
1031            #[results([])]
1032            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1033                type_index = pick_type_index(struct_type_indices, type_index)?;
1034            })]
1035            TakeTypedStructCall { type_index: u32 },
1036
1037            #[operands([Some(Struct(None))])]
1038            #[results([])]
1039            StructLocalSet,
1040
1041            #[operands([])]
1042            #[results([Struct(None)])]
1043            StructLocalGet,
1044
1045            #[operands([Some(Struct(Some(type_index)))])]
1046            #[results([])]
1047            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1048                type_index = pick_type_index(struct_type_indices, type_index)?;
1049            })]
1050            TypedStructLocalSet { type_index: u32 },
1051
1052            #[operands([])]
1053            #[results([Struct(Some(type_index))])]
1054            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1055                type_index = pick_type_index(struct_type_indices, type_index)?;
1056            })]
1057            TypedStructLocalGet { type_index: u32 },
1058
1059            #[operands([Some(Struct(None))])]
1060            #[results([])]
1061            StructGlobalSet,
1062
1063            #[operands([])]
1064            #[results([Struct(None)])]
1065            StructGlobalGet,
1066
1067            #[operands([Some(Struct(Some(type_index)))])]
1068            #[results([])]
1069            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1070                type_index = pick_type_index(struct_type_indices, type_index)?;
1071            })]
1072            TypedStructGlobalSet { type_index: u32 },
1073
1074            #[operands([])]
1075            #[results([Struct(Some(type_index))])]
1076            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1077                type_index = pick_type_index(struct_type_indices, type_index)?;
1078            })]
1079            TypedStructGlobalGet { type_index: u32 },
1080
1081            #[operands([Some(Struct(None))])]
1082            #[results([])]
1083            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1084                // Add one to make sure that out-of-bounds table accesses are
1085                // possible, but still rare.
1086                elem_index = elem_index % (limits.table_size + 1);
1087            })]
1088            StructTableSet { elem_index: u32 },
1089
1090            #[operands([])]
1091            #[results([Struct(None)])]
1092            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1093                // Add one to make sure that out-of-bounds table accesses are
1094                // possible, but still rare.
1095                elem_index = elem_index % (limits.table_size + 1);
1096            })]
1097            StructTableGet { elem_index: u32 },
1098
1099            #[operands([Some(Struct(Some(type_index)))])]
1100            #[results([])]
1101            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1102                // Add one to make sure that out-of-bounds table accesses are
1103                // possible, but still rare.
1104                elem_index = elem_index % (limits.table_size + 1);
1105                type_index = pick_type_index(struct_type_indices, type_index)?;
1106            })]
1107            TypedStructTableSet { elem_index: u32, type_index: u32 },
1108
1109            #[operands([])]
1110            #[results([Struct(Some(type_index))])]
1111            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1112                // Add one to make sure that out-of-bounds table accesses are
1113                // possible, but still rare.
1114                elem_index = elem_index % (limits.table_size + 1);
1115                type_index = pick_type_index(struct_type_indices, type_index)?;
1116            })]
1117            TypedStructTableGet { elem_index: u32, type_index: u32 },
1118
1119            #[operands([None])]
1120            #[results([])]
1121            Drop,
1122
1123            #[operands([])]
1124            #[results([ExternRef])]
1125            NullExtern,
1126
1127            #[operands([])]
1128            #[results([Struct(None)])]
1129            NullStruct,
1130
1131            #[operands([])]
1132            #[results([Eq])]
1133            NullEq,
1134
1135            #[operands([Some(Eq)])]
1136            #[results([])]
1137            TakeEqCall,
1138
1139            #[operands([Some(Eq)])]
1140            #[results([])]
1141            EqLocalSet,
1142
1143            #[operands([])]
1144            #[results([Eq])]
1145            EqLocalGet,
1146
1147            #[operands([Some(Eq)])]
1148            #[results([])]
1149            EqGlobalSet,
1150
1151            #[operands([])]
1152            #[results([Eq])]
1153            EqGlobalGet,
1154
1155            #[operands([Some(Eq)])]
1156            #[results([])]
1157            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1158                // Add one to make sure that out-of-bounds table accesses are
1159                // possible, but still rare.
1160                elem_index = elem_index % (limits.table_size + 1);
1161            })]
1162            EqTableSet { elem_index: u32 },
1163
1164            #[operands([])]
1165            #[results([Eq])]
1166            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1167                // Add one to make sure that out-of-bounds table accesses are
1168                // possible, but still rare.
1169                elem_index = elem_index % (limits.table_size + 1);
1170            })]
1171            EqTableGet { elem_index: u32 },
1172
1173            #[operands([])]
1174            #[results([Struct(Some(type_index))])]
1175            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1176                type_index = pick_type_index(struct_type_indices, type_index)?;
1177            })]
1178            NullTypedStruct { type_index: u32 },
1179
1180            #[operands([Some(Struct(Some(sub_type_index)))])]
1181            #[results([Struct(Some(super_type_index))])]
1182            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1183                sub_type_index = pick_type_index(struct_type_indices, sub_type_index)?;
1184                super_type_index = pick_type_index(struct_type_indices, super_type_index)?;
1185            })]
1186            RefCastUpward { sub_type_index: u32, super_type_index: u32 },
1187
1188            #[operands([Some(Struct(Some(super_type_index)))])]
1189            #[results([Struct(Some(sub_type_index))])]
1190            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1191                sub_type_index = pick_type_index(struct_type_indices, sub_type_index)?;
1192                super_type_index = pick_type_index(struct_type_indices, super_type_index)?;
1193            })]
1194            RefCastDownward { sub_type_index: u32, super_type_index: u32 },
1195
1196            #[operands([Some(Struct(Some(type_index)))])]
1197            #[results([])]
1198            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1199                type_index = pick_type_index(struct_type_indices, type_index)?;
1200            })]
1201            StructGet { type_index: u32, field_index: u32 },
1202
1203            #[operands([Some(Struct(Some(type_index)))])]
1204            #[results([])]
1205            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1206                type_index = pick_type_index(struct_type_indices, type_index)?;
1207            })]
1208            StructGetU { type_index: u32, field_index: u32 },
1209
1210            #[operands([Some(Struct(Some(type_index)))])]
1211            #[results([])]
1212            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1213                type_index = pick_type_index(struct_type_indices, type_index)?;
1214            })]
1215            StructSet { type_index: u32, field_index: u32 },
1216
1217            #[operands([])]
1218            #[results([I31])]
1219            NullI31,
1220
1221            #[operands([])]
1222            #[results([I31])]
1223            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1224                // Any `i32` is a valid operand to `ref.i31` (it wraps to 31
1225                // bits), so no clamping is needed.
1226            })]
1227            RefI31 { value: u32 },
1228
1229            #[operands([Some(I31)])]
1230            #[results([])]
1231            I31LocalSet,
1232
1233            #[operands([])]
1234            #[results([I31])]
1235            I31LocalGet,
1236
1237            #[operands([Some(I31)])]
1238            #[results([])]
1239            I31GlobalSet,
1240
1241            #[operands([])]
1242            #[results([I31])]
1243            I31GlobalGet,
1244
1245            #[operands([Some(I31)])]
1246            #[results([])]
1247            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1248                // Add one to make sure that out-of-bounds table accesses are
1249                // possible, but still rare.
1250                elem_index = elem_index % (limits.table_size + 1);
1251            })]
1252            I31TableSet { elem_index: u32 },
1253
1254            #[operands([])]
1255            #[results([I31])]
1256            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1257                // Add one to make sure that out-of-bounds table accesses are
1258                // possible, but still rare.
1259                elem_index = elem_index % (limits.table_size + 1);
1260            })]
1261            I31TableGet { elem_index: u32 },
1262
1263            #[operands([Some(I31)])]
1264            #[results([])]
1265            TakeI31Call,
1266
1267            #[operands([Some(I31)])]
1268            #[results([])]
1269            I31GetS,
1270
1271            #[operands([Some(I31)])]
1272            #[results([])]
1273            I31GetU,
1274
1275            #[operands([Some(Struct(None))])]
1276            #[results([Eq])]
1277            StructRefAsEq,
1278
1279            #[operands([Some(Struct(Some(type_index)))])]
1280            #[results([Eq])]
1281            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1282                type_index = pick_type_index(struct_type_indices, type_index)?;
1283            })]
1284            TypedStructRefAsEq { type_index: u32 },
1285
1286            #[operands([Some(I31)])]
1287            #[results([Eq])]
1288            I31RefAsEq,
1289
1290            #[operands([])]
1291            #[results([Array(Some(type_index))])]
1292            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1293                type_index = pick_type_index(array_type_indices, type_index)?;
1294            })]
1295            ArrayNewDefault { type_index: u32 },
1296
1297            #[operands([])]
1298            #[results([Array(Some(type_index))])]
1299            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1300                type_index = pick_type_index(array_type_indices, type_index)?;
1301            })]
1302            ArrayNew { type_index: u32 },
1303
1304            #[operands([])]
1305            #[results([Array(Some(type_index))])]
1306            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1307                n = n % (limits.array_length + 1);
1308                type_index = pick_type_index(array_type_indices, type_index)?;
1309            })]
1310            ArrayNewFixed { type_index: u32, n: u32 },
1311
1312            #[operands([])]
1313            #[results([Array(None)])]
1314            NullArray,
1315
1316            #[operands([])]
1317            #[results([Array(Some(type_index))])]
1318            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1319                type_index = pick_type_index(array_type_indices, type_index)?;
1320            })]
1321            NullTypedArray { type_index: u32 },
1322
1323            #[operands([Some(Array(None))])]
1324            #[results([])]
1325            TakeArrayCall,
1326
1327            #[operands([Some(Array(Some(type_index)))])]
1328            #[results([])]
1329            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1330                type_index = pick_type_index(array_type_indices, type_index)?;
1331            })]
1332            TakeTypedArrayCall { type_index: u32 },
1333
1334            #[operands([Some(Array(None))])]
1335            #[results([])]
1336            ArrayLocalSet,
1337
1338            #[operands([])]
1339            #[results([Array(None)])]
1340            ArrayLocalGet,
1341
1342            #[operands([Some(Array(Some(type_index)))])]
1343            #[results([])]
1344            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1345                type_index = pick_type_index(array_type_indices, type_index)?;
1346            })]
1347            TypedArrayLocalSet { type_index: u32 },
1348
1349            #[operands([])]
1350            #[results([Array(Some(type_index))])]
1351            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1352                type_index = pick_type_index(array_type_indices, type_index)?;
1353            })]
1354            TypedArrayLocalGet { type_index: u32 },
1355
1356            #[operands([Some(Array(None))])]
1357            #[results([])]
1358            ArrayGlobalSet,
1359
1360            #[operands([])]
1361            #[results([Array(None)])]
1362            ArrayGlobalGet,
1363
1364            #[operands([Some(Array(Some(type_index)))])]
1365            #[results([])]
1366            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1367                type_index = pick_type_index(array_type_indices, type_index)?;
1368            })]
1369            TypedArrayGlobalSet { type_index: u32 },
1370
1371            #[operands([])]
1372            #[results([Array(Some(type_index))])]
1373            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1374                type_index = pick_type_index(array_type_indices, type_index)?;
1375            })]
1376            TypedArrayGlobalGet { type_index: u32 },
1377
1378            #[operands([Some(Array(None))])]
1379            #[results([])]
1380            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1381                // Add one to make sure that out-of-bounds table accesses are
1382                // possible, but still rare.
1383                elem_index = elem_index % (limits.table_size + 1);
1384            })]
1385            ArrayTableSet { elem_index: u32 },
1386
1387            #[operands([])]
1388            #[results([Array(None)])]
1389            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1390                // Add one to make sure that out-of-bounds table accesses are
1391                // possible, but still rare.
1392                elem_index = elem_index % (limits.table_size + 1);
1393            })]
1394            ArrayTableGet { elem_index: u32 },
1395
1396            #[operands([Some(Array(Some(type_index)))])]
1397            #[results([])]
1398            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1399                // Add one to make sure that out-of-bounds table accesses are
1400                // possible, but still rare.
1401                elem_index = elem_index % (limits.table_size + 1);
1402                type_index = pick_type_index(array_type_indices, type_index)?;
1403            })]
1404            TypedArrayTableSet { elem_index: u32, type_index: u32 },
1405
1406            #[operands([])]
1407            #[results([Array(Some(type_index))])]
1408            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1409                // Add one to make sure that out-of-bounds table accesses are
1410                // possible, but still rare.
1411                elem_index = elem_index % (limits.table_size + 1);
1412                type_index = pick_type_index(array_type_indices, type_index)?;
1413            })]
1414            TypedArrayTableGet { elem_index: u32, type_index: u32 },
1415
1416            #[operands([Some(Array(Some(sub_type_index)))])]
1417            #[results([Array(Some(super_type_index))])]
1418            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1419                sub_type_index = pick_type_index(array_type_indices, sub_type_index)?;
1420                super_type_index = pick_type_index(array_type_indices, super_type_index)?;
1421            })]
1422            ArrayRefCastUpward { sub_type_index: u32, super_type_index: u32 },
1423
1424            #[operands([Some(Array(Some(super_type_index)))])]
1425            #[results([Array(Some(sub_type_index))])]
1426            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1427                sub_type_index = pick_type_index(array_type_indices, sub_type_index)?;
1428                super_type_index = pick_type_index(array_type_indices, super_type_index)?;
1429            })]
1430            ArrayRefCastDownward { sub_type_index: u32, super_type_index: u32 },
1431
1432            #[operands([Some(Array(Some(type_index)))])]
1433            #[results([])]
1434            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1435                // Keep most indices in-bounds: an array is exactly `array_length`
1436                // long, so `% (array_length + 1)` is out-of-bounds only for one
1437                // index value, making OOB traps possible but rare.
1438                index = index % (limits.array_length + 1);
1439                type_index = pick_type_index(array_type_indices, type_index)?;
1440            })]
1441            ArrayGet { type_index: u32, index: u32 },
1442
1443            #[operands([Some(Array(Some(type_index)))])]
1444            #[results([])]
1445            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1446                index = index % (limits.array_length + 1);
1447                type_index = pick_type_index(array_type_indices, type_index)?;
1448            })]
1449            ArrayGetU { type_index: u32, index: u32 },
1450
1451            #[operands([Some(Array(Some(type_index)))])]
1452            #[results([])]
1453            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1454                index = index % (limits.array_length + 1);
1455                type_index = pick_type_index(array_type_indices, type_index)?;
1456            })]
1457            ArraySet { type_index: u32, index: u32 },
1458
1459            #[operands([Some(Array(Some(type_index)))])]
1460            #[results([])]
1461            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1462                offset = offset % (limits.array_length + 1);
1463                len = len % (limits.array_length - offset + 2);
1464                type_index = pick_type_index(array_type_indices, type_index)?;
1465            })]
1466            ArrayFill { type_index: u32, offset: u32, len: u32 },
1467
1468            #[operands([Some(Array(Some(type_index))), Some(Array(Some(type_index)))])]
1469            #[results([])]
1470            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1471                dst_offset = dst_offset % (limits.array_length + 1);
1472                src_offset = src_offset % (limits.array_length + 1);
1473                len = len % (limits.array_length - dst_offset.max(src_offset) + 2);
1474                type_index = pick_type_index(array_type_indices, type_index)?;
1475            })]
1476            ArrayCopy { type_index: u32, dst_offset: u32, src_offset: u32, len: u32 },
1477
1478            #[operands([Some(Array(None))])]
1479            #[results([])]
1480            ArrayLen,
1481
1482            #[operands([Some(Array(None))])]
1483            #[results([Eq])]
1484            ArrayRefAsEq,
1485
1486            #[operands([Some(Array(Some(type_index)))])]
1487            #[results([Eq])]
1488            #[fixup(|limits, num_types, struct_type_indices, array_type_indices| {
1489                type_index = pick_type_index(array_type_indices, type_index)?;
1490            })]
1491            TypedArrayRefAsEq { type_index: u32 },
1492        }
1493    };
1494}
1495
1496macro_rules! define_gc_op_variants {
1497    (
1498        $(
1499            $( #[$attr:meta] )*
1500            $op:ident $( { $( $field:ident : $field_ty:ty ),* } )? ,
1501        )*
1502    ) => {
1503        /// The operations that can be performed by the `gc` function.
1504        #[derive(
1505            Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, mutatis::Mutate,
1506        )]
1507        #[mutatis(default_mutate = false)]
1508        #[allow(missing_docs, reason = "self-describing")]
1509        pub enum GcOp {
1510            $(
1511                $op $( { $( #[mutatis(default_mutate)] $field : $field_ty ),* } )? ,
1512            )*
1513        }
1514    };
1515}
1516for_each_gc_op!(define_gc_op_variants);
1517
1518macro_rules! define_op_names {
1519    (
1520        $(
1521            $( #[$attr:meta] )*
1522            $op:ident $( { $( $field:ident : $field_ty:ty ),* } )? ,
1523        )*
1524    ) => {
1525        #[cfg(test)]
1526        pub(crate) const OP_NAMES: &[&str] = &[
1527            $(stringify!($op)),*
1528        ];
1529    }
1530}
1531for_each_gc_op!(define_op_names);
1532
1533impl GcOp {
1534    /// The variant name, for tests.
1535    #[cfg(test)]
1536    pub(crate) fn name(&self) -> &'static str {
1537        macro_rules! define_gc_op_name {
1538            (
1539                $(
1540                    $( #[$attr:meta] )*
1541                    $op:ident $( { $( $field:ident : $field_ty:ty ),* } )? ,
1542                )*
1543            ) => {
1544                match self {
1545                    $(
1546                        Self::$op $( { $($field: _),* } )? => stringify!($op),
1547                    )*
1548                }
1549            };
1550        }
1551        for_each_gc_op!(define_gc_op_name)
1552    }
1553
1554    /// The types this op pops, with `None` accepting any.
1555    pub(crate) fn operand_types(&self, out: &mut Vec<Option<StackType>>) {
1556        macro_rules! define_gc_op_operand_types {
1557            (
1558                $(
1559                    #[operands($operands:expr)]
1560                    $( #[$attr:meta] )*
1561                    $op:ident $( { $( $field:ident : $field_ty:ty ),* } )? ,
1562                )*
1563            ) => {{
1564                use StackType::*;
1565                match self {
1566                    $(
1567                        Self::$op $( { $($field),* } )? => {
1568                            $(
1569                                $(
1570                                    #[allow(unused, reason = "macro code")]
1571                                    let $field = *$field;
1572                                )*
1573                            )?
1574                            let operands: [Option<StackType>; _] = $operands;
1575                            out.extend(operands);
1576                        }
1577                    )*
1578                }
1579            }};
1580        }
1581        for_each_gc_op!(define_gc_op_operand_types)
1582    }
1583
1584    /// The types this op pushes.
1585    pub(crate) fn result_types(&self, out: &mut Vec<StackType>) {
1586        macro_rules! define_gc_op_result_types {
1587            (
1588                $(
1589                    #[operands($operands:expr)]
1590                    #[results($results:expr)]
1591                    $( #[$attr:meta] )*
1592                    $op:ident $( { $( $field:ident : $field_ty:ty ),* } )? ,
1593                )*
1594            ) => {{
1595                use StackType::*;
1596                match self {
1597                    $(
1598                        Self::$op $( { $($field),* } )? => {
1599                            $(
1600                                $(
1601                                    #[allow(unused, reason = "macro code")]
1602                                    let $field = *$field;
1603                                )*
1604                            )?
1605                            let results: [StackType; _] = $results;
1606                            out.extend(results);
1607                        }
1608                    )*
1609                }
1610            }};
1611        }
1612        for_each_gc_op!(define_gc_op_result_types)
1613    }
1614
1615    /// Fix up an op's immediates. `struct_type_indices` / `array_type_indices`
1616    /// are the concrete encoding indices of each kind; a typed op remaps its
1617    /// type index into the matching set so struct ops never point at an array
1618    /// (or vice versa), and drops itself if no type of that kind exists.
1619    pub(crate) fn fixup_immediates(
1620        &self,
1621        limits: &GcOpsLimits,
1622        num_types: u32,
1623        struct_type_indices: &[u32],
1624        array_type_indices: &[u32],
1625    ) -> Option<Self> {
1626        macro_rules! define_gc_op_fixup {
1627            (
1628                $(
1629                    #[operands($operands:expr)]
1630                    #[results($results:expr)]
1631                    $( #[fixup(|$limits:ident, $num_types:ident, $structs:ident, $arrays:ident| $fixup:expr)] )?
1632                    $op:ident $( { $( $field:ident : $field_ty:ty ),* } )? ,
1633                )*
1634            ) => {{
1635                let _ = (limits, num_types, struct_type_indices, array_type_indices);
1636                match self {
1637                    $(
1638                        Self::$op $( { $($field),* } )? => {
1639                            $(
1640                                $(
1641                                    #[allow(unused_mut, unused_assignments, reason = "macro code")]
1642                                    let mut $field = *$field;
1643                                )*
1644                                #[allow(unused_variables, reason = "macro code")]
1645                                let $limits = limits;
1646                                #[allow(unused_variables, reason = "macro code")]
1647                                let $num_types = num_types;
1648                                #[allow(unused_variables, reason = "macro code")]
1649                                let $structs = struct_type_indices;
1650                                #[allow(unused_variables, reason = "macro code")]
1651                                let $arrays = array_type_indices;
1652                                $fixup;
1653                            )?
1654                            Some(Self::$op $( { $( $field ),* } )? )
1655                        }
1656                    )*
1657                }
1658            }};
1659        }
1660        for_each_gc_op!(define_gc_op_fixup)
1661    }
1662
1663    /// Emit the Wasm instructions of this op into `func`.
1664    fn encode(&self, func: &mut Function, cx: EmitCtx<'_>) {
1665        let bases = cx.bases;
1666        use Dir::{Get, Set};
1667        use RefKind::{Array, Eq, Extern, I31, Struct, Typed};
1668        use Storage::{Global, Local, Table};
1669        match *self {
1670            // Host calls.
1671            Self::Gc => {
1672                func.instruction(&Instruction::Call(bases.funcs.gc));
1673            }
1674            Self::MakeRefs => {
1675                func.instruction(&Instruction::Call(bases.funcs.make_refs));
1676            }
1677            Self::TakeRefs => {
1678                func.instruction(&Instruction::Call(bases.funcs.take_refs));
1679            }
1680            Self::TakeStructCall => {
1681                func.instruction(&Instruction::Call(bases.funcs.take_struct));
1682            }
1683            Self::TakeEqCall => {
1684                func.instruction(&Instruction::Call(bases.funcs.take_eq));
1685            }
1686            Self::TakeArrayCall => {
1687                func.instruction(&Instruction::Call(bases.funcs.take_array));
1688            }
1689            Self::TakeTypedStructCall { type_index } | Self::TakeTypedArrayCall { type_index } => {
1690                func.instruction(&Instruction::Call(bases.funcs.typed(type_index)));
1691            }
1692            Self::TakeI31Call => encode_take_i31(func, bases),
1693
1694            // Nulls and `i31` values.
1695            Self::NullExtern => {
1696                func.instruction(&Instruction::RefNull(HeapType::EXTERN));
1697            }
1698            Self::NullStruct => {
1699                func.instruction(&Instruction::RefNull(STRUCT));
1700            }
1701            Self::NullEq => {
1702                func.instruction(&Instruction::RefNull(EQ));
1703            }
1704            Self::NullI31 => {
1705                func.instruction(&Instruction::RefNull(HeapType::I31));
1706            }
1707            Self::NullArray => {
1708                func.instruction(&Instruction::RefNull(ARRAY));
1709            }
1710            Self::NullTypedStruct { type_index } | Self::NullTypedArray { type_index } => {
1711                let ty = HeapType::Concrete(bases.wasm_type(type_index));
1712                func.instruction(&Instruction::RefNull(ty));
1713            }
1714            Self::RefI31 { value } => {
1715                func.instruction(&Instruction::I32Const(value.cast_signed()));
1716                func.instruction(&Instruction::RefI31);
1717            }
1718
1719            // Allocation.
1720            Self::StructNew { type_index } => encode_struct_new(func, cx, type_index, false),
1721            Self::StructNewDefault { type_index } => encode_struct_new(func, cx, type_index, true),
1722            Self::ArrayNew { type_index } => encode_array_new(func, cx, type_index),
1723            Self::ArrayNewDefault { type_index } => encode_array_new_default(func, cx, type_index),
1724            Self::ArrayNewFixed { type_index, n } => {
1725                encode_array_new_fixed(func, cx, type_index, n)
1726            }
1727
1728            // Casts. Upcasting to `eqref` is implicit in Wasm subtyping, so
1729            // those ops emit nothing; only the abstract stack type changes.
1730            Self::RefCastUpward {
1731                super_type_index, ..
1732            }
1733            | Self::ArrayRefCastUpward {
1734                super_type_index, ..
1735            } => encode_upcast(func, bases, super_type_index),
1736            Self::RefCastDownward {
1737                sub_type_index,
1738                super_type_index,
1739            }
1740            | Self::ArrayRefCastDownward {
1741                sub_type_index,
1742                super_type_index,
1743            } => encode_downcast(func, bases, sub_type_index, super_type_index),
1744            Self::StructRefAsEq
1745            | Self::TypedStructRefAsEq { .. }
1746            | Self::ArrayRefAsEq
1747            | Self::TypedArrayRefAsEq { .. }
1748            | Self::I31RefAsEq => {}
1749
1750            // Field, element and payload access.
1751            Self::StructGet {
1752                type_index,
1753                field_index,
1754            } => encode_struct_get(func, cx, type_index, field_index, false),
1755            Self::StructGetU {
1756                type_index,
1757                field_index,
1758            } => encode_struct_get(func, cx, type_index, field_index, true),
1759            Self::StructSet {
1760                type_index,
1761                field_index,
1762            } => encode_struct_set(func, cx, type_index, field_index),
1763            Self::ArrayGet { type_index, index } => {
1764                encode_array_get(func, cx, type_index, index, false)
1765            }
1766            Self::ArrayGetU { type_index, index } => {
1767                encode_array_get(func, cx, type_index, index, true)
1768            }
1769            Self::ArraySet { type_index, index } => encode_array_set(func, cx, type_index, index),
1770            Self::ArrayFill {
1771                type_index,
1772                offset,
1773                len,
1774            } => encode_array_fill(func, cx, type_index, offset, len),
1775            Self::ArrayCopy {
1776                type_index,
1777                dst_offset,
1778                src_offset,
1779                len,
1780            } => encode_array_copy(func, cx, type_index, dst_offset, src_offset, len),
1781            Self::ArrayLen => encode_array_len(func, bases),
1782            Self::I31GetS => encode_i31_get(func, bases, true),
1783            Self::I31GetU => encode_i31_get(func, bases, false),
1784
1785            Self::Drop => {
1786                func.instruction(&Instruction::Drop);
1787            }
1788
1789            // Root reads and writes; see `encode_root`.
1790            Self::LocalGet { local_index } => {
1791                encode_root(func, bases, Extern { slot: local_index }, Local, Get)
1792            }
1793            Self::LocalSet { local_index } => {
1794                encode_root(func, bases, Extern { slot: local_index }, Local, Set)
1795            }
1796            Self::GlobalGet { global_index } => {
1797                encode_root(func, bases, Extern { slot: global_index }, Global, Get)
1798            }
1799            Self::GlobalSet { global_index } => {
1800                encode_root(func, bases, Extern { slot: global_index }, Global, Set)
1801            }
1802            Self::TableGet { elem_index } => {
1803                encode_root(func, bases, Extern { slot: 0 }, Table(elem_index), Get)
1804            }
1805            Self::TableSet { elem_index } => {
1806                encode_root(func, bases, Extern { slot: 0 }, Table(elem_index), Set)
1807            }
1808
1809            Self::StructLocalGet => encode_root(func, bases, Struct, Local, Get),
1810            Self::StructLocalSet => encode_root(func, bases, Struct, Local, Set),
1811            Self::StructGlobalGet => encode_root(func, bases, Struct, Global, Get),
1812            Self::StructGlobalSet => encode_root(func, bases, Struct, Global, Set),
1813            Self::StructTableGet { elem_index } => {
1814                encode_root(func, bases, Struct, Table(elem_index), Get)
1815            }
1816            Self::StructTableSet { elem_index } => {
1817                encode_root(func, bases, Struct, Table(elem_index), Set)
1818            }
1819
1820            Self::EqLocalGet => encode_root(func, bases, Eq, Local, Get),
1821            Self::EqLocalSet => encode_root(func, bases, Eq, Local, Set),
1822            Self::EqGlobalGet => encode_root(func, bases, Eq, Global, Get),
1823            Self::EqGlobalSet => encode_root(func, bases, Eq, Global, Set),
1824            Self::EqTableGet { elem_index } => encode_root(func, bases, Eq, Table(elem_index), Get),
1825            Self::EqTableSet { elem_index } => encode_root(func, bases, Eq, Table(elem_index), Set),
1826
1827            Self::I31LocalGet => encode_root(func, bases, I31, Local, Get),
1828            Self::I31LocalSet => encode_root(func, bases, I31, Local, Set),
1829            Self::I31GlobalGet => encode_root(func, bases, I31, Global, Get),
1830            Self::I31GlobalSet => encode_root(func, bases, I31, Global, Set),
1831            Self::I31TableGet { elem_index } => {
1832                encode_root(func, bases, I31, Table(elem_index), Get)
1833            }
1834            Self::I31TableSet { elem_index } => {
1835                encode_root(func, bases, I31, Table(elem_index), Set)
1836            }
1837
1838            Self::ArrayLocalGet => encode_root(func, bases, Array, Local, Get),
1839            Self::ArrayLocalSet => encode_root(func, bases, Array, Local, Set),
1840            Self::ArrayGlobalGet => encode_root(func, bases, Array, Global, Get),
1841            Self::ArrayGlobalSet => encode_root(func, bases, Array, Global, Set),
1842            Self::ArrayTableGet { elem_index } => {
1843                encode_root(func, bases, Array, Table(elem_index), Get)
1844            }
1845            Self::ArrayTableSet { elem_index } => {
1846                encode_root(func, bases, Array, Table(elem_index), Set)
1847            }
1848
1849            // Typed struct and array ops share the typed banks; only their
1850            // abstract stack type differs.
1851            Self::TypedStructLocalGet { type_index } | Self::TypedArrayLocalGet { type_index } => {
1852                encode_root(func, bases, Typed(type_index), Local, Get)
1853            }
1854            Self::TypedStructLocalSet { type_index } | Self::TypedArrayLocalSet { type_index } => {
1855                encode_root(func, bases, Typed(type_index), Local, Set)
1856            }
1857            Self::TypedStructGlobalGet { type_index }
1858            | Self::TypedArrayGlobalGet { type_index } => {
1859                encode_root(func, bases, Typed(type_index), Global, Get)
1860            }
1861            Self::TypedStructGlobalSet { type_index }
1862            | Self::TypedArrayGlobalSet { type_index } => {
1863                encode_root(func, bases, Typed(type_index), Global, Set)
1864            }
1865            Self::TypedStructTableGet {
1866                elem_index,
1867                type_index,
1868            }
1869            | Self::TypedArrayTableGet {
1870                elem_index,
1871                type_index,
1872            } => encode_root(func, bases, Typed(type_index), Table(elem_index), Get),
1873            Self::TypedStructTableSet {
1874                elem_index,
1875                type_index,
1876            }
1877            | Self::TypedArrayTableSet {
1878                elem_index,
1879                type_index,
1880            } => encode_root(func, bases, Typed(type_index), Table(elem_index), Set),
1881        };
1882    }
1883}
1884
1885/// A root read or write, one instruction or the `table.set` idiom.
1886fn encode_root(
1887    func: &mut Function,
1888    bases: WasmEncodingBases,
1889    kind: RefKind,
1890    storage: Storage,
1891    dir: Dir,
1892) {
1893    let slots = bases.root_slots(kind);
1894    match (storage, dir) {
1895        (Storage::Local, Dir::Get) => {
1896            func.instruction(&Instruction::LocalGet(slots.local));
1897        }
1898        (Storage::Local, Dir::Set) => {
1899            func.instruction(&Instruction::LocalSet(slots.local));
1900        }
1901        (Storage::Global, Dir::Get) => {
1902            func.instruction(&Instruction::GlobalGet(slots.global));
1903        }
1904        (Storage::Global, Dir::Set) => {
1905            func.instruction(&Instruction::GlobalSet(slots.global));
1906        }
1907        (Storage::Table(elem), Dir::Get) => table_get(func, elem, slots.table),
1908        (Storage::Table(elem), Dir::Set) => table_set_via(func, slots.tmp, elem, slots.table),
1909    }
1910}
1911
1912/// Pass the i31ref and the guest's own `i31.get_s` and `i31.get_u` results to the
1913/// host, which re-derives and compares them.
1914fn encode_take_i31(func: &mut Function, bases: WasmEncodingBases) {
1915    let i31 = bases.locals.i31ref;
1916    func.instruction(&Instruction::LocalTee(i31));
1917    func.instruction(&Instruction::RefIsNull);
1918    func.instruction(&Instruction::If(BlockType::Empty));
1919    // Null branch: `take_i31(null, 0, 0)`.
1920    func.instruction(&Instruction::RefNull(HeapType::I31));
1921    func.instruction(&Instruction::I32Const(0));
1922    func.instruction(&Instruction::I32Const(0));
1923    func.instruction(&Instruction::Call(bases.funcs.take_i31));
1924    func.instruction(&Instruction::Else);
1925    // Non-null branch: `take_i31(ref, i31.get_s, i31.get_u)`.
1926    func.instruction(&Instruction::LocalGet(i31));
1927    func.instruction(&Instruction::LocalGet(i31));
1928    func.instruction(&Instruction::I31GetS);
1929    func.instruction(&Instruction::LocalGet(i31));
1930    func.instruction(&Instruction::I31GetU);
1931    func.instruction(&Instruction::Call(bases.funcs.take_i31));
1932    func.instruction(&Instruction::End);
1933}
1934
1935/// `struct.new` of default field values, or `struct.new_default` when asked and
1936/// every field is defaultable (a non-nullable reference is not).
1937fn encode_struct_new(func: &mut Function, cx: EmitCtx<'_>, type_index: u32, prefer_default: bool) {
1938    let fields = struct_fields(cx.types, cx.encoding_order, type_index).unwrap_or(&[]);
1939    let wasm_type = cx.bases.wasm_type(type_index);
1940    if prefer_default && fields.iter().all(|f| f.field_type.is_defaultable()) {
1941        func.instruction(&Instruction::StructNewDefault(wasm_type));
1942    } else {
1943        for field in fields {
1944            field.field_type.emit_default_const(func, cx);
1945        }
1946        func.instruction(&Instruction::StructNew(wasm_type));
1947    }
1948}
1949
1950/// `array.new` of `array_length` default elements.
1951fn encode_array_new(func: &mut Function, cx: EmitCtx<'_>, type_index: u32) {
1952    if let Some(element) = array_element(cx.types, cx.encoding_order, type_index) {
1953        element.field_type.emit_default_const(func, cx);
1954    }
1955    func.instruction(&Instruction::I32Const(cx.bases.array_length.cast_signed()));
1956    func.instruction(&Instruction::ArrayNew(cx.bases.wasm_type(type_index)));
1957}
1958
1959/// `array.new_default` of `array_length` elements, or `array.new` when the
1960/// element is not defaultable.
1961fn encode_array_new_default(func: &mut Function, cx: EmitCtx<'_>, type_index: u32) {
1962    let wasm_type = cx.bases.wasm_type(type_index);
1963    let len = cx.bases.array_length.cast_signed();
1964    match array_element(cx.types, cx.encoding_order, type_index) {
1965        Some(element) if !element.field_type.is_defaultable() => {
1966            element.field_type.emit_default_const(func, cx);
1967            func.instruction(&Instruction::I32Const(len));
1968            func.instruction(&Instruction::ArrayNew(wasm_type));
1969        }
1970        _ => {
1971            func.instruction(&Instruction::I32Const(len));
1972            func.instruction(&Instruction::ArrayNewDefault(wasm_type));
1973        }
1974    }
1975}
1976
1977/// `array.new_fixed` of `n` default elements.
1978fn encode_array_new_fixed(func: &mut Function, cx: EmitCtx<'_>, type_index: u32, n: u32) {
1979    if let Some(element) = array_element(cx.types, cx.encoding_order, type_index) {
1980        for _ in 0..n {
1981            element.field_type.emit_default_const(func, cx);
1982        }
1983    }
1984    func.instruction(&Instruction::ArrayNewFixed {
1985        array_type_index: cx.bases.wasm_type(type_index),
1986        array_size: n,
1987    });
1988}
1989
1990/// The value on the stack is already the subtype, so this cast always succeeds.
1991fn encode_upcast(func: &mut Function, bases: WasmEncodingBases, super_type_index: u32) {
1992    let heap_type = HeapType::Concrete(bases.wasm_type(super_type_index));
1993    func.instruction(&Instruction::RefCastNullable(heap_type));
1994}
1995
1996/// A downcast that never traps, testing with `ref.test` first and producing `ref.null` on failure.
1997fn encode_downcast(
1998    func: &mut Function,
1999    bases: WasmEncodingBases,
2000    sub_type_index: u32,
2001    super_type_index: u32,
2002) {
2003    let sub_wasm_type = bases.wasm_type(sub_type_index);
2004    let sub_heap_type = HeapType::Concrete(sub_wasm_type);
2005    let temp_local = bases.locals.typed(super_type_index);
2006
2007    func.instruction(&Instruction::LocalTee(temp_local));
2008    func.instruction(&Instruction::RefTestNullable(sub_heap_type));
2009    func.instruction(&Instruction::If(BlockType::Result(ValType::Ref(concrete(
2010        sub_wasm_type,
2011    )))));
2012    func.instruction(&Instruction::LocalGet(temp_local));
2013    func.instruction(&Instruction::RefCastNullable(sub_heap_type));
2014    func.instruction(&Instruction::Else);
2015    func.instruction(&Instruction::RefNull(sub_heap_type));
2016    func.instruction(&Instruction::End);
2017}
2018
2019/// Null-guarded `struct.get` of field `field_index % len`, dropping the value.
2020fn encode_struct_get(
2021    func: &mut Function,
2022    cx: EmitCtx<'_>,
2023    type_index: u32,
2024    field_index: u32,
2025    unsigned: bool,
2026) {
2027    let wasm_type = cx.bases.wasm_type(type_index);
2028    match struct_fields(cx.types, cx.encoding_order, type_index) {
2029        Some(fields) if !fields.is_empty() => {
2030            let typed_local = cx.bases.locals.typed(type_index);
2031            let idx = field_index % u32::try_from(fields.len()).unwrap();
2032            let field_type = fields[usize::try_from(idx).unwrap()].field_type;
2033            let get = struct_get_instruction(wasm_type, idx, field_type, unsigned);
2034            if_non_null(func, typed_local, |func| {
2035                func.instruction(&Instruction::LocalGet(typed_local));
2036                func.instruction(&get);
2037                // Field values are not tracked on the abstract stack.
2038                func.instruction(&Instruction::Drop);
2039            });
2040        }
2041        _ => {
2042            func.instruction(&Instruction::Drop);
2043        }
2044    }
2045}
2046
2047/// Null-guarded `struct.set` of a default value into the first mutable field at or
2048/// after `field_index`, wrapping around, or a drop when no field is mutable.
2049fn encode_struct_set(func: &mut Function, cx: EmitCtx<'_>, type_index: u32, field_index: u32) {
2050    let wasm_type = cx.bases.wasm_type(type_index);
2051    let mutable_field = struct_fields(cx.types, cx.encoding_order, type_index)
2052        .filter(|fields| !fields.is_empty())
2053        .and_then(|fields| {
2054            let len = fields.len();
2055            let start = usize::try_from(field_index).unwrap() % len;
2056            (0..len)
2057                .map(|offset| (start + offset) % len)
2058                .find(|&i| fields[i].mutable)
2059                .map(|i| (u32::try_from(i).unwrap(), fields[i].field_type))
2060        });
2061
2062    match mutable_field {
2063        Some((idx, field_type)) => {
2064            let typed_local = cx.bases.locals.typed(type_index);
2065            if_non_null(func, typed_local, |func| {
2066                func.instruction(&Instruction::LocalGet(typed_local));
2067                field_type.emit_default_const(func, cx);
2068                func.instruction(&Instruction::StructSet {
2069                    struct_type_index: wasm_type,
2070                    field_index: idx,
2071                });
2072            });
2073        }
2074        None => {
2075            func.instruction(&Instruction::Drop);
2076        }
2077    }
2078}
2079
2080/// Null-guarded `array.get`, dropping the element, with `index` kept mostly in bounds by fixup.
2081fn encode_array_get(
2082    func: &mut Function,
2083    cx: EmitCtx<'_>,
2084    type_index: u32,
2085    index: u32,
2086    unsigned: bool,
2087) {
2088    let wasm_type = cx.bases.wasm_type(type_index);
2089    let typed_local = cx.bases.locals.typed(type_index);
2090    match array_element(cx.types, cx.encoding_order, type_index) {
2091        Some(element) => {
2092            let get = array_get_instruction(wasm_type, element.field_type, unsigned);
2093            if_non_null(func, typed_local, |func| {
2094                func.instruction(&Instruction::LocalGet(typed_local));
2095                func.instruction(&Instruction::I32Const(index.cast_signed()));
2096                func.instruction(&get);
2097                // The element value is not tracked on the abstract stack.
2098                func.instruction(&Instruction::Drop);
2099            });
2100        }
2101        None => {
2102            func.instruction(&Instruction::Drop);
2103        }
2104    }
2105}
2106
2107/// Null-guarded `array.set` of a default value, or a drop when the element is immutable.
2108fn encode_array_set(func: &mut Function, cx: EmitCtx<'_>, type_index: u32, index: u32) {
2109    let wasm_type = cx.bases.wasm_type(type_index);
2110    let typed_local = cx.bases.locals.typed(type_index);
2111    match array_element(cx.types, cx.encoding_order, type_index) {
2112        Some(element) if element.mutable => {
2113            if_non_null(func, typed_local, |func| {
2114                func.instruction(&Instruction::LocalGet(typed_local));
2115                func.instruction(&Instruction::I32Const(index.cast_signed()));
2116                element.field_type.emit_default_const(func, cx);
2117                func.instruction(&Instruction::ArraySet(wasm_type));
2118            });
2119        }
2120        _ => {
2121            func.instruction(&Instruction::Drop);
2122        }
2123    }
2124}
2125
2126/// Null-guarded `array.fill` with a default value, or a drop when the element is immutable.
2127fn encode_array_fill(func: &mut Function, cx: EmitCtx<'_>, type_index: u32, offset: u32, len: u32) {
2128    let wasm_type = cx.bases.wasm_type(type_index);
2129    let typed_local = cx.bases.locals.typed(type_index);
2130    match array_element(cx.types, cx.encoding_order, type_index) {
2131        Some(element) if element.mutable => {
2132            if_non_null(func, typed_local, |func| {
2133                func.instruction(&Instruction::LocalGet(typed_local));
2134                func.instruction(&Instruction::I32Const(offset.cast_signed()));
2135                element.field_type.emit_default_const(func, cx);
2136                func.instruction(&Instruction::I32Const(len.cast_signed()));
2137                func.instruction(&Instruction::ArrayFill(wasm_type));
2138            });
2139        }
2140        _ => {
2141            func.instruction(&Instruction::Drop);
2142        }
2143    }
2144}
2145
2146/// `array.copy` between the two arrays on the stack, skipped if either is null.
2147/// The source is parked in the second typed bank while the destination is checked.
2148fn encode_array_copy(
2149    func: &mut Function,
2150    cx: EmitCtx<'_>,
2151    type_index: u32,
2152    dst_offset: u32,
2153    src_offset: u32,
2154    len: u32,
2155) {
2156    let wasm_type = cx.bases.wasm_type(type_index);
2157    let dst_local = cx.bases.locals.typed(type_index);
2158    let src_local = cx.bases.locals.typed2(type_index);
2159    match array_element(cx.types, cx.encoding_order, type_index) {
2160        Some(element) if element.mutable => {
2161            func.instruction(&Instruction::LocalSet(src_local));
2162            func.instruction(&Instruction::LocalSet(dst_local));
2163            func.instruction(&Instruction::LocalGet(dst_local));
2164            func.instruction(&Instruction::RefIsNull);
2165            func.instruction(&Instruction::LocalGet(src_local));
2166            func.instruction(&Instruction::RefIsNull);
2167            func.instruction(&Instruction::I32Or);
2168            func.instruction(&Instruction::If(BlockType::Empty));
2169            func.instruction(&Instruction::Else);
2170            func.instruction(&Instruction::LocalGet(dst_local));
2171            func.instruction(&Instruction::I32Const(dst_offset.cast_signed()));
2172            func.instruction(&Instruction::LocalGet(src_local));
2173            func.instruction(&Instruction::I32Const(src_offset.cast_signed()));
2174            func.instruction(&Instruction::I32Const(len.cast_signed()));
2175            func.instruction(&Instruction::ArrayCopy {
2176                array_type_index_dst: wasm_type,
2177                array_type_index_src: wasm_type,
2178            });
2179            func.instruction(&Instruction::End);
2180        }
2181        _ => {
2182            func.instruction(&Instruction::Drop);
2183            func.instruction(&Instruction::Drop);
2184        }
2185    }
2186}
2187
2188/// Null-guarded `array.len`, dropping the length.
2189fn encode_array_len(func: &mut Function, bases: WasmEncodingBases) {
2190    let array_local = bases.locals.arrayref;
2191    if_non_null(func, array_local, |func| {
2192        func.instruction(&Instruction::LocalGet(array_local));
2193        func.instruction(&Instruction::ArrayLen);
2194        func.instruction(&Instruction::Drop);
2195    });
2196}
2197
2198/// Null-guarded `i31.get_s` or `i31.get_u`, dropping the value.
2199fn encode_i31_get(func: &mut Function, bases: WasmEncodingBases, signed: bool) {
2200    let i31_local = bases.locals.i31ref;
2201    let get = if signed {
2202        Instruction::I31GetS
2203    } else {
2204        Instruction::I31GetU
2205    };
2206    if_non_null(func, i31_local, |func| {
2207        func.instruction(&Instruction::LocalGet(i31_local));
2208        func.instruction(&get);
2209        func.instruction(&Instruction::Drop);
2210    });
2211}