Skip to main content

wasmtime_environ/fact/
trampoline.rs

1//! Low-level compilation of an fused adapter function.
2//!
3//! This module is tasked with the top-level `compile` function which creates a
4//! single WebAssembly function which will perform the steps of the fused
5//! adapter for an `AdapterData` provided. This is the "meat" of compilation
6//! where the validation of the canonical ABI or similar all happens to
7//! translate arguments from one module to another.
8//!
9//! ## Traps and their ordering
10//!
11//! Currently this compiler is pretty "loose" about the ordering of precisely
12//! what trap happens where. The main reason for this is that to core wasm all
13//! traps are the same and for fused adapters if a trap happens no intermediate
14//! side effects are visible (as designed by the canonical ABI itself). For this
15//! it's important to note that some of the precise choices of control flow here
16//! can be somewhat arbitrary, an intentional decision.
17
18use crate::component::{
19    CanonicalAbiInfo, ComponentTypesBuilder, FLAG_MAY_LEAVE, FixedEncoding as FE, FlatType,
20    InterfaceType, MAX_FLAT_ASYNC_PARAMS, MAX_FLAT_PARAMS, PREPARE_ASYNC_NO_RESULT,
21    PREPARE_ASYNC_WITH_RESULT, START_FLAG_ASYNC_CALLEE, StringEncoding, Transcode,
22    TypeComponentLocalErrorContextTableIndex, TypeEnumIndex, TypeFixedLengthListIndex,
23    TypeFlagsIndex, TypeFutureTableIndex, TypeListIndex, TypeMapIndex, TypeOptionIndex,
24    TypeRecordIndex, TypeResourceTableIndex, TypeResultIndex, TypeStreamTableIndex, TypeTupleIndex,
25    TypeVariantIndex, VariantInfo,
26};
27use crate::fact::signature::Signature;
28use crate::fact::transcode::Transcoder;
29use crate::fact::{
30    AdapterData, Body, Function, FunctionId, Helper, HelperLocation, HelperType,
31    LinearMemoryOptions, Module, Options,
32};
33use crate::prelude::*;
34use crate::{FuncIndex, GlobalIndex, IndexType, Trap};
35use std::collections::HashMap;
36use std::mem;
37use std::ops::Range;
38use wasm_encoder::{BlockType, Encode, Instruction, Instruction::*, MemArg, ValType};
39use wasmtime_component_util::{DiscriminantSize, FlagsSize};
40
41use super::DataModel;
42
43const MAX_STRING_BYTE_LENGTH: u32 = (1 << 31) - 1;
44const UTF16_TAG: u32 = 1 << 31;
45
46/// This value is arbitrarily chosen and should be fine to change at any time,
47/// it just seemed like a halfway reasonable starting point.
48const INITIAL_FUEL: usize = 1_000;
49
50struct Compiler<'a, 'b> {
51    types: &'a ComponentTypesBuilder,
52    module: &'b mut Module<'a>,
53    result: FunctionId,
54
55    /// The encoded WebAssembly function body so far, not including locals.
56    code: Vec<u8>,
57
58    /// Total number of locals generated so far.
59    nlocals: u32,
60
61    /// Locals partitioned by type which are not currently in use.
62    free_locals: HashMap<ValType, Vec<u32>>,
63
64    /// A heuristic which is intended to limit the size of a generated function
65    /// to a certain maximum to avoid generating arbitrarily large functions.
66    ///
67    /// This fuel counter is decremented each time `translate` is called and
68    /// when fuel is entirely consumed further translations, if necessary, will
69    /// be done through calls to other functions in the module. This is intended
70    /// to be a heuristic to split up the main function into theoretically
71    /// reusable portions.
72    fuel: usize,
73
74    /// Indicates whether an "enter call" should be emitted in the generated
75    /// function with a call to `Resource{Enter,Exit}Call` at the beginning and
76    /// end of the function for tracking of information related to borrowed
77    /// resources.
78    emit_resource_call: bool,
79}
80
81pub(super) fn compile(module: &mut Module<'_>, adapter: &AdapterData) {
82    fn compiler<'a, 'b>(
83        module: &'b mut Module<'a>,
84        adapter: &AdapterData,
85    ) -> (Compiler<'a, 'b>, Signature, Signature) {
86        let lower_sig = module.types.signature(&adapter.lower);
87        let lift_sig = module.types.signature(&adapter.lift);
88        let ty = module
89            .core_types
90            .function(&lower_sig.params, &lower_sig.results);
91        let result = module
92            .funcs
93            .push(Function::new(Some(adapter.name.clone()), ty));
94
95        // If this type signature contains any borrowed resources then invocations
96        // of enter/exit call for resource-related metadata tracking must be used.
97        // It shouldn't matter whether the lower/lift signature is used here as both
98        // should return the same answer.
99        let emit_resource_call = module.types.contains_borrow_resource(&adapter.lower);
100        assert_eq!(
101            emit_resource_call,
102            module.types.contains_borrow_resource(&adapter.lift)
103        );
104
105        (
106            Compiler::new(
107                module,
108                result,
109                lower_sig.params.len() as u32,
110                emit_resource_call,
111            ),
112            lower_sig,
113            lift_sig,
114        )
115    }
116
117    // If the lift and lower instances are equal, or if one is an ancestor of
118    // the other, we trap unconditionally.  This ensures that recursive
119    // reentrance via an adapter is impossible.
120    if adapter.lift.instance == adapter.lower.instance
121        || adapter.lower.ancestors.contains(&adapter.lift.instance)
122        || adapter.lift.ancestors.contains(&adapter.lower.instance)
123    {
124        let (mut compiler, _, _) = compiler(module, adapter);
125        compiler.trap(Trap::CannotEnterComponent);
126        compiler.finish();
127        return;
128    }
129
130    // This closure compiles a function to be exported to the host which host to
131    // lift the parameters from the caller and lower them to the callee.
132    //
133    // This allows the host to delay copying the parameters until the callee
134    // signals readiness by clearing its backpressure flag.
135    let async_start_adapter = |module: &mut Module| {
136        let sig = module
137            .types
138            .async_start_signature(&adapter.lower, &adapter.lift);
139        let ty = module.core_types.function(&sig.params, &sig.results);
140        let result = module.funcs.push(Function::new(
141            Some(format!("[async-start]{}", adapter.name)),
142            ty,
143        ));
144
145        Compiler::new(module, result, sig.params.len() as u32, false)
146            .compile_async_start_adapter(adapter, &sig);
147
148        result
149    };
150
151    // This closure compiles a function to be exported by the adapter module and
152    // called by the host to lift the results from the callee and lower them to
153    // the caller.
154    //
155    // Given that async-lifted exports return their results via the
156    // `task.return` intrinsic, the host will need to copy the results from
157    // callee to caller when that intrinsic is called rather than when the
158    // callee task fully completes (which may happen much later).
159    let async_return_adapter = |module: &mut Module| {
160        let sig = module
161            .types
162            .async_return_signature(&adapter.lower, &adapter.lift);
163        let ty = module.core_types.function(&sig.params, &sig.results);
164        let result = module.funcs.push(Function::new(
165            Some(format!("[async-return]{}", adapter.name)),
166            ty,
167        ));
168
169        Compiler::new(module, result, sig.params.len() as u32, false)
170            .compile_async_return_adapter(adapter, &sig);
171
172        result
173    };
174
175    match (adapter.lower.options.async_, adapter.lift.options.async_) {
176        (false, false) => {
177            // We can adapt sync->sync case with only minimal use of intrinsics,
178            // e.g. resource enter and exit calls as needed.
179            let (compiler, lower_sig, lift_sig) = compiler(module, adapter);
180            compiler.compile_sync_to_sync_adapter(adapter, &lower_sig, &lift_sig)
181        }
182        (true, true) => {
183            assert!(module.tunables.concurrency_support);
184
185            // In the async->async case, we must compile a couple of helper functions:
186            //
187            // - `async-start`: copies the parameters from the caller to the callee
188            // - `async-return`: copies the result from the callee to the caller
189            //
190            // Unlike synchronous calls, the above operations are asynchronous
191            // and subject to backpressure.  If the callee is not yet ready to
192            // handle a new call, the `async-start` function will not be called
193            // immediately.  Instead, control will return to the caller,
194            // allowing it to do other work while waiting for this call to make
195            // progress.  Once the callee indicates it is ready, `async-start`
196            // will be called, and sometime later (possibly after various task
197            // switch events), when the callee has produced a result, it will
198            // call `async-return` via the `task.return` intrinsic, at which
199            // point a `STATUS_RETURNED` event will be delivered to the caller.
200            let start = async_start_adapter(module);
201            let return_ = async_return_adapter(module);
202            let (compiler, lower_sig, lift_sig) = compiler(module, adapter);
203            compiler.compile_async_to_async_adapter(
204                adapter,
205                start,
206                return_,
207                i32::try_from(lift_sig.params.len()).unwrap(),
208                &lower_sig,
209            );
210        }
211        (false, true) => {
212            assert!(module.tunables.concurrency_support);
213
214            // Like the async->async case above, for the sync->async case we
215            // also need `async-start` and `async-return` helper functions to
216            // allow the callee to asynchronously "pull" the parameters and
217            // "push" the results when it is ready.
218            //
219            // However, since the caller is using the synchronous ABI, the
220            // parameters may have been passed via the stack rather than linear
221            // memory.  In that case, we pass them to the host to store in a
222            // task-local location temporarily in the case of backpressure.
223            // Similarly, the host will also temporarily store the results that
224            // the callee provides to `async-return` until it is ready to resume
225            // the caller.
226            let start = async_start_adapter(module);
227            let return_ = async_return_adapter(module);
228            let (compiler, lower_sig, lift_sig) = compiler(module, adapter);
229            compiler.compile_sync_to_async_adapter(
230                adapter,
231                start,
232                return_,
233                i32::try_from(lift_sig.params.len()).unwrap(),
234                &lower_sig,
235            );
236        }
237        (true, false) => {
238            assert!(module.tunables.concurrency_support);
239
240            // As with the async->async and sync->async cases above, for the
241            // async->sync case we use `async-start` and `async-return` helper
242            // functions.  Here, those functions allow the host to enforce
243            // backpressure in the case where the callee instance already has
244            // another synchronous call in progress, in which case we can't
245            // start a new one until the current one (and any others already
246            // waiting in line behind it) has completed.
247            //
248            // In the case of backpressure, we'll return control to the caller
249            // immediately so it can do other work.  Later, once the callee is
250            // ready, the host will call the `async-start` function to retrieve
251            // the parameters and pass them to the callee.  At that point, the
252            // callee may block on a host call, at which point the host will
253            // suspend the fiber it is running on and allow the caller (or any
254            // other ready instance) to run concurrently with the blocked
255            // callee.  Once the callee finally returns, the host will call the
256            // `async-return` function to write the result to the caller's
257            // linear memory and deliver a `STATUS_RETURNED` event to the
258            // caller.
259            let lift_sig = module.types.signature(&adapter.lift);
260            let start = async_start_adapter(module);
261            let return_ = async_return_adapter(module);
262            let (compiler, lower_sig, ..) = compiler(module, adapter);
263            compiler.compile_async_to_sync_adapter(
264                adapter,
265                start,
266                return_,
267                i32::try_from(lift_sig.params.len()).unwrap(),
268                i32::try_from(lift_sig.results.len()).unwrap(),
269                &lower_sig,
270            );
271        }
272    }
273}
274
275/// Compiles a helper function as specified by the `Helper` configuration.
276///
277/// This function is invoked when the translation process runs out of fuel for
278/// some prior function which enqueues a helper to get translated later. This
279/// translation function will perform one type translation as specified by
280/// `Helper` which can either be in the stack or memory for each side.
281pub(super) fn compile_helper(module: &mut Module<'_>, result: FunctionId, helper: Helper) {
282    let mut nlocals = 0;
283    let src_flat;
284    let src = match helper.src.loc {
285        // If the source is on the stack then it's specified in the parameters
286        // to the function, so this creates the flattened representation and
287        // then lists those as the locals with appropriate types for the source
288        // values.
289        HelperLocation::Stack => {
290            src_flat = module
291                .types
292                .flatten_types(&helper.src.opts, usize::MAX, [helper.src.ty])
293                .unwrap()
294                .iter()
295                .enumerate()
296                .map(|(i, ty)| (i as u32, *ty))
297                .collect::<Vec<_>>();
298            nlocals += src_flat.len() as u32;
299            Source::Stack(Stack {
300                locals: &src_flat,
301                opts: &helper.src.opts,
302            })
303        }
304        // If the source is in memory then that's just propagated here as the
305        // first local is the pointer to the source.
306        HelperLocation::Memory => {
307            nlocals += 1;
308            Source::Memory(Memory {
309                opts: &helper.src.opts,
310                addr: TempLocal::new(0, helper.src.opts.data_model.unwrap_memory().ptr()),
311                offset: 0,
312            })
313        }
314        HelperLocation::StructField | HelperLocation::ArrayElement => todo!("CM+GC"),
315    };
316    let dst_flat;
317    let dst = match helper.dst.loc {
318        // This is the same as the stack-based source although `Destination` is
319        // configured slightly differently.
320        HelperLocation::Stack => {
321            dst_flat = module
322                .types
323                .flatten_types(&helper.dst.opts, usize::MAX, [helper.dst.ty])
324                .unwrap();
325            Destination::Stack(&dst_flat, &helper.dst.opts)
326        }
327        // This is the same as a memory-based source but note that the address
328        // of the destination is passed as the final parameter to the function.
329        HelperLocation::Memory => {
330            nlocals += 1;
331            Destination::Memory(Memory {
332                opts: &helper.dst.opts,
333                addr: TempLocal::new(
334                    nlocals - 1,
335                    helper.dst.opts.data_model.unwrap_memory().ptr(),
336                ),
337                offset: 0,
338            })
339        }
340        HelperLocation::StructField | HelperLocation::ArrayElement => todo!("CM+GC"),
341    };
342    let mut compiler = Compiler {
343        types: module.types,
344        module,
345        code: Vec::new(),
346        nlocals,
347        free_locals: HashMap::new(),
348        result,
349        fuel: INITIAL_FUEL,
350        // This is a helper function and only the top-level function is
351        // responsible for emitting these intrinsic calls.
352        emit_resource_call: false,
353    };
354    compiler.translate(&helper.src.ty, &src, &helper.dst.ty, &dst);
355    compiler.finish();
356}
357
358/// Possible ways that a interface value is represented in the core wasm
359/// canonical ABI.
360enum Source<'a> {
361    /// This value is stored on the "stack" in wasm locals.
362    ///
363    /// This could mean that it's inline from the parameters to the function or
364    /// that after a function call the results were stored in locals and the
365    /// locals are the inline results.
366    Stack(Stack<'a>),
367
368    /// This value is stored in linear memory described by the `Memory`
369    /// structure.
370    Memory(Memory<'a>),
371
372    /// This value is stored in a GC struct field described by the `GcStruct`
373    /// structure.
374    #[allow(dead_code, reason = "CM+GC is still WIP")]
375    Struct(GcStruct<'a>),
376
377    /// This value is stored in a GC array element described by the `GcArray`
378    /// structure.
379    #[allow(dead_code, reason = "CM+GC is still WIP")]
380    Array(GcArray<'a>),
381}
382
383/// Same as `Source` but for where values are translated into.
384enum Destination<'a> {
385    /// This value is destined for the WebAssembly stack which means that
386    /// results are simply pushed as we go along.
387    ///
388    /// The types listed are the types that are expected to be on the stack at
389    /// the end of translation.
390    Stack(&'a [ValType], &'a Options),
391
392    /// This value is to be placed in linear memory described by `Memory`.
393    Memory(Memory<'a>),
394
395    /// This value is to be placed in a GC struct field described by the
396    /// `GcStruct` structure.
397    #[allow(dead_code, reason = "CM+GC is still WIP")]
398    Struct(GcStruct<'a>),
399
400    /// This value is to be placed in a GC array element described by the
401    /// `GcArray` structure.
402    #[allow(dead_code, reason = "CM+GC is still WIP")]
403    Array(GcArray<'a>),
404}
405
406struct Stack<'a> {
407    /// The locals that comprise a particular value.
408    ///
409    /// The length of this list represents the flattened list of types that make
410    /// up the component value. Each list has the index of the local being
411    /// accessed as well as the type of the local itself.
412    locals: &'a [(u32, ValType)],
413    /// The lifting/lowering options for where this stack of values comes from
414    opts: &'a Options,
415}
416
417/// Representation of where a value is going to be stored in linear memory.
418struct Memory<'a> {
419    /// The lifting/lowering options with memory configuration
420    opts: &'a Options,
421    /// The index of the local that contains the base address of where the
422    /// storage is happening.
423    addr: TempLocal,
424    /// A "static" offset that will be baked into wasm instructions for where
425    /// memory loads/stores happen.
426    offset: u32,
427}
428
429impl<'a> Memory<'a> {
430    fn mem_opts(&self) -> &'a LinearMemoryOptions {
431        self.opts.data_model.unwrap_memory()
432    }
433}
434
435/// Representation of where a value is coming from or going to in a GC struct.
436struct GcStruct<'a> {
437    opts: &'a Options,
438    // TODO: more fields to come in the future.
439}
440
441/// Representation of where a value is coming from or going to in a GC array.
442struct GcArray<'a> {
443    opts: &'a Options,
444    // TODO: more fields to come in the future.
445}
446
447impl<'a, 'b> Compiler<'a, 'b> {
448    fn new(
449        module: &'b mut Module<'a>,
450        result: FunctionId,
451        nlocals: u32,
452        emit_resource_call: bool,
453    ) -> Self {
454        Self {
455            types: module.types,
456            module,
457            result,
458            code: Vec::new(),
459            nlocals,
460            free_locals: HashMap::new(),
461            fuel: INITIAL_FUEL,
462            emit_resource_call,
463        }
464    }
465
466    /// Compile an adapter function supporting an async-lowered import to an
467    /// async-lifted export.
468    ///
469    /// This uses a pair of `async-prepare` and `async-start` built-in functions
470    /// to set up and start a subtask, respectively.  `async-prepare` accepts
471    /// `start` and `return_` functions which copy the parameters and results,
472    /// respectively; the host will call the former when the callee has cleared
473    /// its backpressure flag and the latter when the callee has called
474    /// `task.return`.
475    fn compile_async_to_async_adapter(
476        mut self,
477        adapter: &AdapterData,
478        start: FunctionId,
479        return_: FunctionId,
480        param_count: i32,
481        lower_sig: &Signature,
482    ) {
483        let start_call =
484            self.module
485                .import_async_start_call(&adapter.name, adapter.lift.options.callback, None);
486
487        self.call_prepare(adapter, start, return_, lower_sig, false);
488
489        // TODO: As an optimization, consider checking the backpressure flag on
490        // the callee instance and, if it's unset _and_ the callee uses a
491        // callback, translate the params and call the callee function directly
492        // here (and make sure `start_call` knows _not_ to call it in that case).
493
494        // We export this function so we can pass a funcref to the host.
495        //
496        // TODO: Use a declarative element segment instead of exporting this.
497        self.module.exports.push((
498            adapter.callee.as_u32(),
499            format!("[adapter-callee]{}", adapter.name),
500        ));
501
502        self.instruction(RefFunc(adapter.callee.as_u32()));
503        self.instruction(I32Const(param_count));
504        // The result count for an async callee is either one (if there's a
505        // callback) or zero (if there's no callback).  We conservatively use
506        // one here to ensure the host provides room for the result, if any.
507        self.instruction(I32Const(1));
508        self.instruction(I32Const(START_FLAG_ASYNC_CALLEE));
509        self.instruction(Call(start_call.as_u32()));
510
511        self.finish()
512    }
513
514    /// Invokes the `prepare_call` builtin with the provided parameters for this
515    /// adapter.
516    ///
517    /// This is part of a async lower and/or async lift adapter. This is not
518    /// used for a sync->sync function call. This is done to create the task on
519    /// the host side of the runtime and such. This will notably invoke a
520    /// Cranelift builtin which will spill all wasm-level parameters to the
521    /// stack to handle variadic signatures.
522    ///
523    /// Note that the `prepare_sync` parameter here configures the
524    /// `result_count_or_max_if_async` parameter to indicate whether this is a
525    /// sync or async prepare.
526    fn call_prepare(
527        &mut self,
528        adapter: &AdapterData,
529        start: FunctionId,
530        return_: FunctionId,
531        lower_sig: &Signature,
532        prepare_sync: bool,
533    ) {
534        let prepare = self.module.import_prepare_call(
535            &adapter.name,
536            &lower_sig.params,
537            match adapter.lift.options.data_model {
538                DataModel::Gc {} => todo!("CM+GC"),
539                DataModel::LinearMemory(LinearMemoryOptions { memory, .. }) => memory.map(|m| m.0),
540            },
541        );
542
543        self.flush_code();
544        self.module.funcs[self.result]
545            .body
546            .push(Body::RefFunc(start));
547        self.module.funcs[self.result]
548            .body
549            .push(Body::RefFunc(return_));
550        self.instruction(I32Const(
551            i32::try_from(adapter.lower.instance.as_u32()).unwrap(),
552        ));
553        self.instruction(I32Const(
554            i32::try_from(adapter.lift.instance.as_u32()).unwrap(),
555        ));
556        self.instruction(I32Const(
557            i32::try_from(self.types[adapter.lift.ty].results.as_u32()).unwrap(),
558        ));
559        self.instruction(I32Const(if self.types[adapter.lift.ty].async_ {
560            1
561        } else {
562            0
563        }));
564        self.instruction(I32Const(i32::from(
565            adapter.lift.options.string_encoding as u8,
566        )));
567
568        // flag this as a preparation for either an async call or sync call,
569        // depending on `prepare_sync`
570        let result_types = &self.types[self.types[adapter.lower.ty].results].types;
571        if prepare_sync {
572            self.instruction(I32Const(
573                i32::try_from(
574                    self.types
575                        .flatten_types(
576                            &adapter.lower.options,
577                            usize::MAX,
578                            result_types.iter().copied(),
579                        )
580                        .map(|v| v.len())
581                        .unwrap_or(usize::try_from(i32::MAX).unwrap()),
582                )
583                .unwrap(),
584            ));
585        } else {
586            if result_types.len() > 0 {
587                self.instruction(I32Const(PREPARE_ASYNC_WITH_RESULT.cast_signed()));
588            } else {
589                self.instruction(I32Const(PREPARE_ASYNC_NO_RESULT.cast_signed()));
590            }
591        }
592
593        // forward all our own arguments on to the host stub
594        for index in 0..lower_sig.params.len() {
595            self.instruction(LocalGet(u32::try_from(index).unwrap()));
596        }
597        self.instruction(Call(prepare.as_u32()));
598    }
599
600    /// Compile an adapter function supporting a sync-lowered import to an
601    /// async-lifted export.
602    ///
603    /// This uses a pair of `sync-prepare` and `sync-start` built-in functions
604    /// to set up and start a subtask, respectively.  `sync-prepare` accepts
605    /// `start` and `return_` functions which copy the parameters and results,
606    /// respectively; the host will call the former when the callee has cleared
607    /// its backpressure flag and the latter when the callee has called
608    /// `task.return`.
609    fn compile_sync_to_async_adapter(
610        mut self,
611        adapter: &AdapterData,
612        start: FunctionId,
613        return_: FunctionId,
614        lift_param_count: i32,
615        lower_sig: &Signature,
616    ) {
617        let start_call = self.module.import_sync_start_call(
618            &adapter.name,
619            adapter.lift.options.callback,
620            &lower_sig.results,
621        );
622
623        self.call_prepare(adapter, start, return_, lower_sig, true);
624
625        // TODO: As an optimization, consider checking the backpressure flag on
626        // the callee instance and, if it's unset _and_ the callee uses a
627        // callback, translate the params and call the callee function directly
628        // here (and make sure `start_call` knows _not_ to call it in that case).
629
630        // We export this function so we can pass a funcref to the host.
631        //
632        // TODO: Use a declarative element segment instead of exporting this.
633        self.module.exports.push((
634            adapter.callee.as_u32(),
635            format!("[adapter-callee]{}", adapter.name),
636        ));
637
638        self.instruction(RefFunc(adapter.callee.as_u32()));
639        self.instruction(I32Const(lift_param_count));
640        self.instruction(Call(start_call.as_u32()));
641
642        self.finish()
643    }
644
645    /// Compile an adapter function supporting an async-lowered import to a
646    /// sync-lifted export.
647    ///
648    /// This uses a pair of `async-prepare` and `async-start` built-in functions
649    /// to set up and start a subtask, respectively.  `async-prepare` accepts
650    /// `start` and `return_` functions which copy the parameters and results,
651    /// respectively; the host will call the former when the callee has cleared
652    /// its backpressure flag and the latter when the callee has returned its
653    /// result(s).
654    fn compile_async_to_sync_adapter(
655        mut self,
656        adapter: &AdapterData,
657        start: FunctionId,
658        return_: FunctionId,
659        param_count: i32,
660        result_count: i32,
661        lower_sig: &Signature,
662    ) {
663        let start_call =
664            self.module
665                .import_async_start_call(&adapter.name, None, adapter.lift.post_return);
666
667        self.call_prepare(adapter, start, return_, lower_sig, false);
668
669        // We export this function so we can pass a funcref to the host.
670        //
671        // TODO: Use a declarative element segment instead of exporting this.
672        self.module.exports.push((
673            adapter.callee.as_u32(),
674            format!("[adapter-callee]{}", adapter.name),
675        ));
676
677        self.instruction(RefFunc(adapter.callee.as_u32()));
678        self.instruction(I32Const(param_count));
679        self.instruction(I32Const(result_count));
680        self.instruction(I32Const(0));
681        self.instruction(Call(start_call.as_u32()));
682
683        self.finish()
684    }
685
686    /// Compiles a function to be exported to the host which host to lift the
687    /// parameters from the caller and lower them to the callee.
688    ///
689    /// This allows the host to delay copying the parameters until the callee
690    /// signals readiness by clearing its backpressure flag.
691    fn compile_async_start_adapter(mut self, adapter: &AdapterData, sig: &Signature) {
692        let param_locals = sig
693            .params
694            .iter()
695            .enumerate()
696            .map(|(i, ty)| (i as u32, *ty))
697            .collect::<Vec<_>>();
698
699        self.set_flag(adapter.lift.flags, FLAG_MAY_LEAVE, false);
700        self.translate_params(adapter, &param_locals);
701        self.set_flag(adapter.lift.flags, FLAG_MAY_LEAVE, true);
702
703        self.finish();
704    }
705
706    /// Compiles a function to be exported by the adapter module and called by
707    /// the host to lift the results from the callee and lower them to the
708    /// caller.
709    ///
710    /// Given that async-lifted exports return their results via the
711    /// `task.return` intrinsic, the host will need to copy the results from
712    /// callee to caller when that intrinsic is called rather than when the
713    /// callee task fully completes (which may happen much later).
714    fn compile_async_return_adapter(mut self, adapter: &AdapterData, sig: &Signature) {
715        let param_locals = sig
716            .params
717            .iter()
718            .enumerate()
719            .map(|(i, ty)| (i as u32, *ty))
720            .collect::<Vec<_>>();
721
722        self.set_flag(adapter.lower.flags, FLAG_MAY_LEAVE, false);
723        // Note that we pass `param_locals` as _both_ the `param_locals` and
724        // `result_locals` parameters to `translate_results`.  That's because
725        // the _parameters_ to `task.return` are actually the _results_ that the
726        // caller is waiting for.
727        //
728        // Additionally, the host will append a return
729        // pointer to the end of that list before calling this adapter's
730        // `async-return` function if the results exceed `MAX_FLAT_RESULTS` or
731        // the import is lowered async, in which case `translate_results` will
732        // use that pointer to store the results.
733        self.translate_results(adapter, &param_locals, &param_locals);
734        self.set_flag(adapter.lower.flags, FLAG_MAY_LEAVE, true);
735
736        self.finish()
737    }
738
739    /// Compile an adapter function supporting a sync-lowered import to a
740    /// sync-lifted export.
741    ///
742    /// Unlike calls involving async-lowered imports or async-lifted exports,
743    /// this adapter need not involve host built-ins except possibly for
744    /// resource bookkeeping.
745    fn compile_sync_to_sync_adapter(
746        mut self,
747        adapter: &AdapterData,
748        lower_sig: &Signature,
749        lift_sig: &Signature,
750    ) {
751        // Check the instance flags required for this trampoline.
752        //
753        // This inserts the initial check required by `canon_lower` that the
754        // caller instance can be left and additionally checks the
755        // flags on the callee if necessary whether it can be entered.
756        self.trap_if_not_flag(
757            adapter.lower.flags,
758            FLAG_MAY_LEAVE,
759            Trap::CannotLeaveComponent,
760        );
761
762        let old_task_may_block = if self.module.tunables.concurrency_support {
763            // Save, clear, and later restore the `may_block` field.
764            let task_may_block = self.module.import_task_may_block();
765            let old_task_may_block = if self.types[adapter.lift.ty].async_ {
766                self.instruction(GlobalGet(task_may_block.as_u32()));
767                self.instruction(I32Eqz);
768                self.instruction(If(BlockType::Empty));
769                self.trap(Trap::CannotBlockSyncTask);
770                self.instruction(End);
771                None
772            } else {
773                let task_may_block = self.module.import_task_may_block();
774                self.instruction(GlobalGet(task_may_block.as_u32()));
775                let old_task_may_block = self.local_set_new_tmp(ValType::I32);
776                self.instruction(I32Const(0));
777                self.instruction(GlobalSet(task_may_block.as_u32()));
778                Some(old_task_may_block)
779            };
780
781            // Push a task onto the current task stack.
782            //
783            // FIXME: Apply the optimizations described in #12311.
784
785            self.instruction(I32Const(
786                i32::try_from(adapter.lower.instance.as_u32()).unwrap(),
787            ));
788            self.instruction(I32Const(if self.types[adapter.lift.ty].async_ {
789                1
790            } else {
791                0
792            }));
793            self.instruction(I32Const(
794                i32::try_from(adapter.lift.instance.as_u32()).unwrap(),
795            ));
796            let enter_sync_call = self.module.import_enter_sync_call();
797            self.instruction(Call(enter_sync_call.as_u32()));
798
799            old_task_may_block
800        } else if self.emit_resource_call {
801            let enter_sync_call = self.module.import_enter_sync_call();
802            self.instruction(Call(enter_sync_call.as_u32()));
803            None
804        } else {
805            None
806        };
807
808        // Perform the translation of arguments. Note that `FLAG_MAY_LEAVE` is
809        // cleared around this invocation for the callee as per the
810        // `canon_lift` definition in the spec. Additionally note that the
811        // precise ordering of traps here is not required since internal state
812        // is not visible to either instance and a trap will "lock down" both
813        // instances to no longer be visible. This means that we're free to
814        // reorder lifts/lowers and flags and such as is necessary and
815        // convenient here.
816        //
817        // TODO: if translation doesn't actually call any functions in either
818        // instance then there's no need to set/clear the flag here and that can
819        // be optimized away.
820        self.set_flag(adapter.lift.flags, FLAG_MAY_LEAVE, false);
821        let param_locals = lower_sig
822            .params
823            .iter()
824            .enumerate()
825            .map(|(i, ty)| (i as u32, *ty))
826            .collect::<Vec<_>>();
827        self.translate_params(adapter, &param_locals);
828        self.set_flag(adapter.lift.flags, FLAG_MAY_LEAVE, true);
829
830        // With all the arguments on the stack the actual target function is
831        // now invoked. The core wasm results of the function are then placed
832        // into locals for result translation afterwards.
833        self.instruction(Call(adapter.callee.as_u32()));
834        let mut result_locals = Vec::with_capacity(lift_sig.results.len());
835        let mut temps = Vec::new();
836        for ty in lift_sig.results.iter().rev() {
837            let local = self.local_set_new_tmp(*ty);
838            result_locals.push((local.idx, *ty));
839            temps.push(local);
840        }
841        result_locals.reverse();
842
843        // Handle a few things related to the concurrent task infrastructure
844        // after the callee has finished, such as:
845        //
846        // * Validate that the callee dropped all its borrows
847        // * Transition the current running task back to the caller.
848        //
849        // This is not necessary if there are no resources in this call, nor if
850        // concurrency support is disabled (no tasks). Note that this must
851        // happen before lowering below because semantically that's where the
852        // "you forgot to drop borrows" trap shows up and additionally the
853        // lowering below may call realloc which is in the context of the
854        // caller's task, not the callee.
855        //
856        // FIXME: Apply the optimizations described in #12311.
857        if self.emit_resource_call || self.module.tunables.concurrency_support {
858            let exit_sync_call = self.module.import_exit_sync_call();
859            self.instruction(Call(exit_sync_call.as_u32()));
860        }
861
862        // Like above during the translation of results the caller cannot be
863        // left (as we might invoke things like `realloc`). Again the precise
864        // order of everything doesn't matter since intermediate states cannot
865        // be witnessed, hence the setting of flags here to encapsulate both
866        // liftings and lowerings.
867        //
868        // TODO: like above the management of the `MAY_LEAVE` flag can probably
869        // be elided here for "simple" results.
870        self.set_flag(adapter.lower.flags, FLAG_MAY_LEAVE, false);
871        self.translate_results(adapter, &param_locals, &result_locals);
872        self.set_flag(adapter.lower.flags, FLAG_MAY_LEAVE, true);
873
874        // And finally post-return state is handled here once all results/etc
875        // are all translated.
876        if let Some(func) = adapter.lift.post_return {
877            for (result, _) in result_locals.iter() {
878                self.instruction(LocalGet(*result));
879            }
880            self.instruction(Call(func.as_u32()));
881        }
882
883        for tmp in temps {
884            self.free_temp_local(tmp);
885        }
886
887        if self.module.tunables.concurrency_support {
888            // Restore old `may_block_field`
889            if let Some(old_task_may_block) = old_task_may_block {
890                let task_may_block = self.module.import_task_may_block();
891                self.instruction(LocalGet(old_task_may_block.idx));
892                self.instruction(GlobalSet(task_may_block.as_u32()));
893                self.free_temp_local(old_task_may_block);
894            }
895        }
896
897        self.finish()
898    }
899
900    fn translate_params(&mut self, adapter: &AdapterData, param_locals: &[(u32, ValType)]) {
901        let src_tys = self.types[adapter.lower.ty].params;
902        let src_tys = self.types[src_tys]
903            .types
904            .iter()
905            .copied()
906            .collect::<Vec<_>>();
907        let dst_tys = self.types[adapter.lift.ty].params;
908        let dst_tys = self.types[dst_tys]
909            .types
910            .iter()
911            .copied()
912            .collect::<Vec<_>>();
913        let lift_opts = &adapter.lift.options;
914        let lower_opts = &adapter.lower.options;
915
916        // TODO: handle subtyping
917        assert_eq!(src_tys.len(), dst_tys.len());
918
919        // Async lowered functions have a smaller limit on flat parameters, but
920        // their destination, a lifted function, does not have a different limit
921        // than sync functions.
922        let max_flat_params = if adapter.lower.options.async_ {
923            MAX_FLAT_ASYNC_PARAMS
924        } else {
925            MAX_FLAT_PARAMS
926        };
927        let src_flat =
928            self.types
929                .flatten_types(lower_opts, max_flat_params, src_tys.iter().copied());
930        let dst_flat =
931            self.types
932                .flatten_types(lift_opts, MAX_FLAT_PARAMS, dst_tys.iter().copied());
933
934        let src = if let Some(flat) = &src_flat {
935            Source::Stack(Stack {
936                locals: &param_locals[..flat.len()],
937                opts: lower_opts,
938            })
939        } else {
940            // If there are too many parameters then that means the parameters
941            // are actually a tuple stored in linear memory addressed by the
942            // first parameter local.
943            let lower_mem_opts = lower_opts.data_model.unwrap_memory();
944            let (addr, ty) = param_locals[0];
945            assert_eq!(ty, lower_mem_opts.ptr());
946            let abi = CanonicalAbiInfo::record(src_tys.iter().map(|t| self.types.canonical_abi(t)));
947            Source::Memory(self.memory_operand_abi(
948                lower_opts,
949                TempLocal::new(addr, ty),
950                &abi,
951                Trap::MemoryOutOfBounds,
952            ))
953        };
954
955        let dst = if let Some(flat) = &dst_flat {
956            Destination::Stack(flat, lift_opts)
957        } else {
958            // If there are too many parameters then space is allocated in the
959            // destination module for the parameters via its `realloc` function.
960            let abi = CanonicalAbiInfo::record(dst_tys.iter().map(|t| self.types.canonical_abi(t)));
961            Destination::Memory(self.malloc_abi(lift_opts, &abi, Trap::MemoryOutOfBounds))
962        };
963
964        let srcs = src
965            .record_field_srcs(self.types, src_tys.iter().copied())
966            .zip(src_tys.iter());
967        let dsts = dst
968            .record_field_dsts(self.types, dst_tys.iter().copied())
969            .zip(dst_tys.iter());
970        for ((src, src_ty), (dst, dst_ty)) in srcs.zip(dsts) {
971            self.translate(&src_ty, &src, &dst_ty, &dst);
972        }
973
974        // If the destination was linear memory instead of the stack then the
975        // actual parameter that we're passing is the address of the values
976        // stored, so ensure that's happening in the wasm body here.
977        if let Destination::Memory(mem) = dst {
978            self.instruction(LocalGet(mem.addr.idx));
979            self.free_temp_local(mem.addr);
980        }
981    }
982
983    fn translate_results(
984        &mut self,
985        adapter: &AdapterData,
986        param_locals: &[(u32, ValType)],
987        result_locals: &[(u32, ValType)],
988    ) {
989        let src_tys = self.types[adapter.lift.ty].results;
990        let src_tys = self.types[src_tys]
991            .types
992            .iter()
993            .copied()
994            .collect::<Vec<_>>();
995        let dst_tys = self.types[adapter.lower.ty].results;
996        let dst_tys = self.types[dst_tys]
997            .types
998            .iter()
999            .copied()
1000            .collect::<Vec<_>>();
1001        let lift_opts = &adapter.lift.options;
1002        let lower_opts = &adapter.lower.options;
1003
1004        let src_flat = self
1005            .types
1006            .flatten_lifting_types(lift_opts, src_tys.iter().copied());
1007        let dst_flat = self
1008            .types
1009            .flatten_lowering_types(lower_opts, dst_tys.iter().copied());
1010
1011        let src = if src_flat.is_some() {
1012            Source::Stack(Stack {
1013                locals: result_locals,
1014                opts: lift_opts,
1015            })
1016        } else {
1017            // The original results to read from in this case come from the
1018            // return value of the function itself. The imported function will
1019            // return a linear memory address at which the values can be read
1020            // from.
1021            let abi = CanonicalAbiInfo::record(src_tys.iter().map(|t| self.types.canonical_abi(t)));
1022            assert_eq!(
1023                result_locals.len(),
1024                if lower_opts.async_ || lift_opts.async_ {
1025                    2
1026                } else {
1027                    1
1028                }
1029            );
1030            let (addr, ty) = result_locals[0];
1031            assert_eq!(ty, lift_opts.data_model.unwrap_memory().ptr());
1032            Source::Memory(self.memory_operand_abi(
1033                lift_opts,
1034                TempLocal::new(addr, ty),
1035                &abi,
1036                Trap::MemoryOutOfBounds,
1037            ))
1038        };
1039
1040        let dst = if let Some(flat) = &dst_flat {
1041            Destination::Stack(flat, lower_opts)
1042        } else {
1043            // This is slightly different than `translate_params` where the
1044            // return pointer was provided by the caller of this function
1045            // meaning the last parameter local is a pointer into linear memory.
1046            let abi = CanonicalAbiInfo::record(dst_tys.iter().map(|t| self.types.canonical_abi(t)));
1047            let (addr, ty) = *param_locals.last().expect("no retptr");
1048            assert_eq!(ty, lower_opts.data_model.unwrap_memory().ptr());
1049            Destination::Memory(self.memory_operand_abi(
1050                lower_opts,
1051                TempLocal::new(addr, ty),
1052                &abi,
1053                Trap::MemoryOutOfBounds,
1054            ))
1055        };
1056
1057        let srcs = src
1058            .record_field_srcs(self.types, src_tys.iter().copied())
1059            .zip(src_tys.iter());
1060        let dsts = dst
1061            .record_field_dsts(self.types, dst_tys.iter().copied())
1062            .zip(dst_tys.iter());
1063        for ((src, src_ty), (dst, dst_ty)) in srcs.zip(dsts) {
1064            self.translate(&src_ty, &src, &dst_ty, &dst);
1065        }
1066    }
1067
1068    fn translate(
1069        &mut self,
1070        src_ty: &InterfaceType,
1071        src: &Source<'_>,
1072        dst_ty: &InterfaceType,
1073        dst: &Destination,
1074    ) {
1075        if let Source::Memory(mem) = src {
1076            self.assert_aligned(src_ty, mem);
1077        }
1078        if let Destination::Memory(mem) = dst {
1079            self.assert_aligned(dst_ty, mem);
1080        }
1081
1082        // Calculate a cost heuristic for what the translation of this specific
1083        // layer of the type is going to incur. The purpose of this cost is that
1084        // we'll deduct it from `self.fuel` and if no fuel is remaining then
1085        // translation is outlined into a separate function rather than being
1086        // translated into this function.
1087        //
1088        // The general goal is to avoid creating an exponentially sized function
1089        // for a linearly sized input (the type section). By outlining helper
1090        // functions there will ideally be a constant set of helper functions
1091        // per type (to accommodate in-memory or on-stack transfers as well as
1092        // src/dst options) which means that each function is at most a certain
1093        // size and we have a linear number of functions which should guarantee
1094        // an overall linear size of the output.
1095        //
1096        // To implement this the current heuristic is that each layer of
1097        // translating a type has a cost associated with it and this cost is
1098        // accounted for in `self.fuel`. Some conversions are considered free as
1099        // they generate basically as much code as the `call` to the translation
1100        // function while other are considered proportionally expensive to the
1101        // size of the type. The hope is that some upper layers are of a type's
1102        // translation are all inlined into one function but bottom layers end
1103        // up getting outlined to separate functions. Theoretically, again this
1104        // is built on hopes and dreams, the outlining can be shared amongst
1105        // tightly-intertwined type hierarchies which will reduce the size of
1106        // the output module due to the helpers being used.
1107        //
1108        // This heuristic of how to split functions has changed a few times in
1109        // the past and this isn't necessarily guaranteed to be the final
1110        // iteration.
1111        let cost = match src_ty {
1112            // These types are all quite simple to load/store and equate to
1113            // basically the same cost of the `call` instruction to call an
1114            // out-of-line translation function, so give them 0 cost.
1115            InterfaceType::Bool
1116            | InterfaceType::U8
1117            | InterfaceType::S8
1118            | InterfaceType::U16
1119            | InterfaceType::S16
1120            | InterfaceType::U32
1121            | InterfaceType::S32
1122            | InterfaceType::U64
1123            | InterfaceType::S64
1124            | InterfaceType::Float32
1125            | InterfaceType::Float64 => 0,
1126
1127            // This has a small amount of validation associated with it, so
1128            // give it a cost of 1.
1129            InterfaceType::Char => 1,
1130
1131            // This has a fair bit of code behind it depending on the
1132            // strings/encodings in play, so arbitrarily assign it this cost.
1133            InterfaceType::String => 40,
1134
1135            // Iteration of a loop is along the lines of the cost of a string
1136            // so give it the same cost
1137            InterfaceType::List(_) => 40,
1138            // Maps are similar to lists in terms of iteration cost
1139            InterfaceType::Map(_) => 40,
1140
1141            InterfaceType::Flags(i) => {
1142                let count = self.module.types[*i].names.len();
1143                match FlagsSize::from_count(count) {
1144                    FlagsSize::Size0 => 0,
1145                    FlagsSize::Size1 | FlagsSize::Size2 => 1,
1146                    FlagsSize::Size4Plus(n) => n.into(),
1147                }
1148            }
1149
1150            InterfaceType::Record(i) => self.types[*i].fields.len(),
1151            InterfaceType::Tuple(i) => self.types[*i].types.len(),
1152            InterfaceType::Variant(i) => self.types[*i].cases.len(),
1153            InterfaceType::Enum(i) => self.types[*i].names.len(),
1154
1155            // 2 cases to consider for each of these variants.
1156            InterfaceType::Option(_) | InterfaceType::Result(_) => 2,
1157
1158            // TODO(#6696) - something nonzero, is 1 right?
1159            InterfaceType::Own(_)
1160            | InterfaceType::Borrow(_)
1161            | InterfaceType::Future(_)
1162            | InterfaceType::Stream(_)
1163            | InterfaceType::ErrorContext(_) => 1,
1164            InterfaceType::FixedLengthList(i) => self.types[*i].size as usize,
1165        };
1166
1167        match self.fuel.checked_sub(cost) {
1168            // This function has enough fuel to perform the layer of translation
1169            // necessary for this type, so the fuel is updated in-place and
1170            // translation continues. Note that the recursion here is bounded by
1171            // the static recursion limit for all interface types as imposed
1172            // during the translation phase.
1173            Some(n) => {
1174                self.fuel = n;
1175                match src_ty {
1176                    InterfaceType::Bool => self.translate_bool(src, dst_ty, dst),
1177                    InterfaceType::U8 => self.translate_u8(src, dst_ty, dst),
1178                    InterfaceType::S8 => self.translate_s8(src, dst_ty, dst),
1179                    InterfaceType::U16 => self.translate_u16(src, dst_ty, dst),
1180                    InterfaceType::S16 => self.translate_s16(src, dst_ty, dst),
1181                    InterfaceType::U32 => self.translate_u32(src, dst_ty, dst),
1182                    InterfaceType::S32 => self.translate_s32(src, dst_ty, dst),
1183                    InterfaceType::U64 => self.translate_u64(src, dst_ty, dst),
1184                    InterfaceType::S64 => self.translate_s64(src, dst_ty, dst),
1185                    InterfaceType::Float32 => self.translate_f32(src, dst_ty, dst),
1186                    InterfaceType::Float64 => self.translate_f64(src, dst_ty, dst),
1187                    InterfaceType::Char => self.translate_char(src, dst_ty, dst),
1188                    InterfaceType::String => self.translate_string(src, dst_ty, dst),
1189                    InterfaceType::List(t) => self.translate_list(*t, src, dst_ty, dst),
1190                    InterfaceType::Map(t) => self.translate_map(*t, src, dst_ty, dst),
1191                    InterfaceType::Record(t) => self.translate_record(*t, src, dst_ty, dst),
1192                    InterfaceType::Flags(f) => self.translate_flags(*f, src, dst_ty, dst),
1193                    InterfaceType::Tuple(t) => self.translate_tuple(*t, src, dst_ty, dst),
1194                    InterfaceType::Variant(v) => self.translate_variant(*v, src, dst_ty, dst),
1195                    InterfaceType::Enum(t) => self.translate_enum(*t, src, dst_ty, dst),
1196                    InterfaceType::Option(t) => self.translate_option(*t, src, dst_ty, dst),
1197                    InterfaceType::Result(t) => self.translate_result(*t, src, dst_ty, dst),
1198                    InterfaceType::Own(t) => self.translate_own(*t, src, dst_ty, dst),
1199                    InterfaceType::Borrow(t) => self.translate_borrow(*t, src, dst_ty, dst),
1200                    InterfaceType::Future(t) => self.translate_future(*t, src, dst_ty, dst),
1201                    InterfaceType::Stream(t) => self.translate_stream(*t, src, dst_ty, dst),
1202                    InterfaceType::ErrorContext(t) => {
1203                        self.translate_error_context(*t, src, dst_ty, dst)
1204                    }
1205                    InterfaceType::FixedLengthList(t) => {
1206                        self.translate_fixed_length_list(*t, src, dst_ty, dst);
1207                    }
1208                }
1209            }
1210
1211            // This function does not have enough fuel left to perform this
1212            // layer of translation so the translation is deferred to a helper
1213            // function. The actual translation here is then done by marshalling
1214            // the src/dst into the function we're calling and then processing
1215            // the results.
1216            None => {
1217                let src_loc = match src {
1218                    // If the source is on the stack then `stack_get` is used to
1219                    // convert everything to the appropriate flat representation
1220                    // for the source type.
1221                    Source::Stack(stack) => {
1222                        for (i, ty) in stack
1223                            .opts
1224                            .flat_types(src_ty, self.types)
1225                            .unwrap()
1226                            .iter()
1227                            .enumerate()
1228                        {
1229                            let stack = stack.slice(i..i + 1);
1230                            self.stack_get(&stack, (*ty).into());
1231                        }
1232                        HelperLocation::Stack
1233                    }
1234                    // If the source is in memory then the pointer is passed
1235                    // through, but note that the offset must be factored in
1236                    // here since the translation function will start from
1237                    // offset 0.
1238                    Source::Memory(mem) => {
1239                        self.push_mem_addr(mem);
1240                        HelperLocation::Memory
1241                    }
1242                    Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1243                };
1244                let dst_loc = match dst {
1245                    Destination::Stack(..) => HelperLocation::Stack,
1246                    Destination::Memory(mem) => {
1247                        self.push_mem_addr(mem);
1248                        HelperLocation::Memory
1249                    }
1250                    Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1251                };
1252                // Generate a `FunctionId` corresponding to the `Helper`
1253                // configuration that is necessary here. This will ideally be a
1254                // "cache hit" and use a preexisting helper which represents
1255                // outlining what would otherwise be duplicate code within a
1256                // function to one function.
1257                let helper = self.module.translate_helper(Helper {
1258                    src: HelperType {
1259                        ty: *src_ty,
1260                        opts: *src.opts(),
1261                        loc: src_loc,
1262                    },
1263                    dst: HelperType {
1264                        ty: *dst_ty,
1265                        opts: *dst.opts(),
1266                        loc: dst_loc,
1267                    },
1268                });
1269                // Emit a `call` instruction which will get "relocated" to a
1270                // function index once translation has completely finished.
1271                self.flush_code();
1272                self.module.funcs[self.result].body.push(Body::Call(helper));
1273
1274                // If the destination of the translation was on the stack then
1275                // the types on the stack need to be optionally converted to
1276                // different types (e.g. if the result here is part of a variant
1277                // somewhere else).
1278                //
1279                // This translation happens inline here by popping the results
1280                // into new locals and then using those locals to do a
1281                // `stack_set`.
1282                if let Destination::Stack(tys, opts) = dst {
1283                    let flat = self
1284                        .types
1285                        .flatten_types(opts, usize::MAX, [*dst_ty])
1286                        .unwrap();
1287                    assert_eq!(flat.len(), tys.len());
1288                    let locals = flat
1289                        .iter()
1290                        .rev()
1291                        .map(|ty| self.local_set_new_tmp(*ty))
1292                        .collect::<Vec<_>>();
1293                    for (ty, local) in tys.iter().zip(locals.into_iter().rev()) {
1294                        self.instruction(LocalGet(local.idx));
1295                        self.stack_set(std::slice::from_ref(ty), local.ty);
1296                        self.free_temp_local(local);
1297                    }
1298                }
1299            }
1300        }
1301    }
1302
1303    fn push_mem_addr(&mut self, mem: &Memory<'_>) {
1304        self.instruction(LocalGet(mem.addr.idx));
1305        if mem.offset != 0 {
1306            self.ptr_uconst(mem.mem_opts(), mem.offset);
1307            self.ptr_add(mem.mem_opts());
1308        }
1309    }
1310
1311    fn translate_bool(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1312        // TODO: subtyping
1313        assert!(matches!(dst_ty, InterfaceType::Bool));
1314        self.push_dst_addr(dst);
1315
1316        // Booleans are canonicalized to 0 or 1 as they pass through the
1317        // component boundary, so use a `select` instruction to do so.
1318        self.instruction(I32Const(1));
1319        self.instruction(I32Const(0));
1320        match src {
1321            Source::Memory(mem) => self.i32_load8u(mem),
1322            Source::Stack(stack) => self.stack_get(stack, ValType::I32),
1323            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1324        }
1325        self.instruction(Select);
1326
1327        match dst {
1328            Destination::Memory(mem) => self.i32_store8(mem),
1329            Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1330            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1331        }
1332    }
1333
1334    fn translate_u8(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1335        // TODO: subtyping
1336        assert!(matches!(dst_ty, InterfaceType::U8));
1337        self.convert_u8_mask(src, dst, 0xff);
1338    }
1339
1340    fn convert_u8_mask(&mut self, src: &Source<'_>, dst: &Destination<'_>, mask: u8) {
1341        self.push_dst_addr(dst);
1342        let mut needs_mask = true;
1343        match src {
1344            Source::Memory(mem) => {
1345                self.i32_load8u(mem);
1346                needs_mask = mask != 0xff;
1347            }
1348            Source::Stack(stack) => {
1349                self.stack_get(stack, ValType::I32);
1350            }
1351            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1352        }
1353        if needs_mask {
1354            self.instruction(I32Const(i32::from(mask)));
1355            self.instruction(I32And);
1356        }
1357        match dst {
1358            Destination::Memory(mem) => self.i32_store8(mem),
1359            Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1360            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1361        }
1362    }
1363
1364    fn translate_s8(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1365        // TODO: subtyping
1366        assert!(matches!(dst_ty, InterfaceType::S8));
1367        self.push_dst_addr(dst);
1368        match src {
1369            Source::Memory(mem) => self.i32_load8s(mem),
1370            Source::Stack(stack) => {
1371                self.stack_get(stack, ValType::I32);
1372                self.instruction(I32Extend8S);
1373            }
1374            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1375        }
1376        match dst {
1377            Destination::Memory(mem) => self.i32_store8(mem),
1378            Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1379            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1380        }
1381    }
1382
1383    fn translate_u16(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1384        // TODO: subtyping
1385        assert!(matches!(dst_ty, InterfaceType::U16));
1386        self.convert_u16_mask(src, dst, 0xffff);
1387    }
1388
1389    fn convert_u16_mask(&mut self, src: &Source<'_>, dst: &Destination<'_>, mask: u16) {
1390        self.push_dst_addr(dst);
1391        let mut needs_mask = true;
1392        match src {
1393            Source::Memory(mem) => {
1394                self.i32_load16u(mem);
1395                needs_mask = mask != 0xffff;
1396            }
1397            Source::Stack(stack) => {
1398                self.stack_get(stack, ValType::I32);
1399            }
1400            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1401        }
1402        if needs_mask {
1403            self.instruction(I32Const(i32::from(mask)));
1404            self.instruction(I32And);
1405        }
1406        match dst {
1407            Destination::Memory(mem) => self.i32_store16(mem),
1408            Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1409            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1410        }
1411    }
1412
1413    fn translate_s16(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1414        // TODO: subtyping
1415        assert!(matches!(dst_ty, InterfaceType::S16));
1416        self.push_dst_addr(dst);
1417        match src {
1418            Source::Memory(mem) => self.i32_load16s(mem),
1419            Source::Stack(stack) => {
1420                self.stack_get(stack, ValType::I32);
1421                self.instruction(I32Extend16S);
1422            }
1423            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1424        }
1425        match dst {
1426            Destination::Memory(mem) => self.i32_store16(mem),
1427            Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1428            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1429        }
1430    }
1431
1432    fn translate_u32(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1433        // TODO: subtyping
1434        assert!(matches!(dst_ty, InterfaceType::U32));
1435        self.convert_u32_mask(src, dst, 0xffffffff)
1436    }
1437
1438    fn convert_u32_mask(&mut self, src: &Source<'_>, dst: &Destination<'_>, mask: u32) {
1439        self.push_dst_addr(dst);
1440        match src {
1441            Source::Memory(mem) => self.i32_load(mem),
1442            Source::Stack(stack) => self.stack_get(stack, ValType::I32),
1443            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1444        }
1445        if mask != 0xffffffff {
1446            self.instruction(I32Const(mask as i32));
1447            self.instruction(I32And);
1448        }
1449        match dst {
1450            Destination::Memory(mem) => self.i32_store(mem),
1451            Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1452            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1453        }
1454    }
1455
1456    fn translate_s32(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1457        // TODO: subtyping
1458        assert!(matches!(dst_ty, InterfaceType::S32));
1459        self.push_dst_addr(dst);
1460        match src {
1461            Source::Memory(mem) => self.i32_load(mem),
1462            Source::Stack(stack) => self.stack_get(stack, ValType::I32),
1463            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1464        }
1465        match dst {
1466            Destination::Memory(mem) => self.i32_store(mem),
1467            Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1468            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1469        }
1470    }
1471
1472    fn translate_u64(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1473        // TODO: subtyping
1474        assert!(matches!(dst_ty, InterfaceType::U64));
1475        self.push_dst_addr(dst);
1476        match src {
1477            Source::Memory(mem) => self.i64_load(mem),
1478            Source::Stack(stack) => self.stack_get(stack, ValType::I64),
1479            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1480        }
1481        match dst {
1482            Destination::Memory(mem) => self.i64_store(mem),
1483            Destination::Stack(stack, _) => self.stack_set(stack, ValType::I64),
1484            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1485        }
1486    }
1487
1488    fn translate_s64(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1489        // TODO: subtyping
1490        assert!(matches!(dst_ty, InterfaceType::S64));
1491        self.push_dst_addr(dst);
1492        match src {
1493            Source::Memory(mem) => self.i64_load(mem),
1494            Source::Stack(stack) => self.stack_get(stack, ValType::I64),
1495            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1496        }
1497        match dst {
1498            Destination::Memory(mem) => self.i64_store(mem),
1499            Destination::Stack(stack, _) => self.stack_set(stack, ValType::I64),
1500            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1501        }
1502    }
1503
1504    fn translate_f32(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1505        // TODO: subtyping
1506        assert!(matches!(dst_ty, InterfaceType::Float32));
1507        self.push_dst_addr(dst);
1508        match src {
1509            Source::Memory(mem) => self.f32_load(mem),
1510            Source::Stack(stack) => self.stack_get(stack, ValType::F32),
1511            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1512        }
1513        match dst {
1514            Destination::Memory(mem) => self.f32_store(mem),
1515            Destination::Stack(stack, _) => self.stack_set(stack, ValType::F32),
1516            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1517        }
1518    }
1519
1520    fn translate_f64(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1521        // TODO: subtyping
1522        assert!(matches!(dst_ty, InterfaceType::Float64));
1523        self.push_dst_addr(dst);
1524        match src {
1525            Source::Memory(mem) => self.f64_load(mem),
1526            Source::Stack(stack) => self.stack_get(stack, ValType::F64),
1527            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1528        }
1529        match dst {
1530            Destination::Memory(mem) => self.f64_store(mem),
1531            Destination::Stack(stack, _) => self.stack_set(stack, ValType::F64),
1532            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1533        }
1534    }
1535
1536    fn translate_char(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1537        assert!(matches!(dst_ty, InterfaceType::Char));
1538        match src {
1539            Source::Memory(mem) => self.i32_load(mem),
1540            Source::Stack(stack) => self.stack_get(stack, ValType::I32),
1541            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1542        }
1543        let local = self.local_set_new_tmp(ValType::I32);
1544
1545        // This sequence is copied from the output of LLVM for:
1546        //
1547        //      pub extern "C" fn foo(x: u32) -> char {
1548        //          char::try_from(x)
1549        //              .unwrap_or_else(|_| std::arch::wasm32::unreachable())
1550        //      }
1551        //
1552        // Apparently this does what's required by the canonical ABI:
1553        //
1554        //    def i32_to_char(opts, i):
1555        //      trap_if(i >= 0x110000)
1556        //      trap_if(0xD800 <= i <= 0xDFFF)
1557        //      return chr(i)
1558        //
1559        // ... but I don't know how it works other than "well I trust LLVM"
1560        self.instruction(Block(BlockType::Empty));
1561        self.instruction(Block(BlockType::Empty));
1562        self.instruction(LocalGet(local.idx));
1563        self.instruction(I32Const(0xd800));
1564        self.instruction(I32Xor);
1565        self.instruction(I32Const(-0x110000));
1566        self.instruction(I32Add);
1567        self.instruction(I32Const(-0x10f800));
1568        self.instruction(I32LtU);
1569        self.instruction(BrIf(0));
1570        self.instruction(LocalGet(local.idx));
1571        self.instruction(I32Const(0x110000));
1572        self.instruction(I32Ne);
1573        self.instruction(BrIf(1));
1574        self.instruction(End);
1575        self.trap(Trap::InvalidChar);
1576        self.instruction(End);
1577
1578        self.push_dst_addr(dst);
1579        self.instruction(LocalGet(local.idx));
1580        match dst {
1581            Destination::Memory(mem) => {
1582                self.i32_store(mem);
1583            }
1584            Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1585            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1586        }
1587
1588        self.free_temp_local(local);
1589    }
1590
1591    fn translate_string(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1592        assert!(matches!(dst_ty, InterfaceType::String));
1593        let src_opts = src.opts();
1594        let dst_opts = dst.opts();
1595
1596        let src_mem_opts = match &src_opts.data_model {
1597            DataModel::Gc {} => todo!("CM+GC"),
1598            DataModel::LinearMemory(opts) => opts,
1599        };
1600        let dst_mem_opts = match &dst_opts.data_model {
1601            DataModel::Gc {} => todo!("CM+GC"),
1602            DataModel::LinearMemory(opts) => opts,
1603        };
1604
1605        // Load the pointer/length of this string into temporary locals. These
1606        // will be referenced a good deal so this just makes it easier to deal
1607        // with them consistently below rather than trying to reload from memory
1608        // for example.
1609        match src {
1610            Source::Stack(s) => {
1611                assert_eq!(s.locals.len(), 2);
1612                self.stack_get(&s.slice(0..1), src_mem_opts.ptr());
1613                self.stack_get(&s.slice(1..2), src_mem_opts.ptr());
1614            }
1615            Source::Memory(mem) => {
1616                self.ptr_load(mem);
1617                self.ptr_load(&mem.bump(src_mem_opts.ptr_size().into()));
1618            }
1619            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1620        }
1621        let src_len = self.local_set_new_tmp(src_mem_opts.ptr());
1622        let src_ptr = self.local_set_new_tmp(src_mem_opts.ptr());
1623        let src_str = WasmString {
1624            ptr: src_ptr,
1625            len: src_len,
1626            opts: src_opts,
1627        };
1628
1629        let dst_str = match src_opts.string_encoding {
1630            StringEncoding::Utf8 => {
1631                self.validate_guest_pointer(
1632                    src_opts,
1633                    &src_str.ptr,
1634                    &AllocSize::Local(src_str.len.idx),
1635                    1,
1636                    Trap::StringOutOfBounds,
1637                );
1638                match dst_opts.string_encoding {
1639                    StringEncoding::Utf8 => {
1640                        self.string_copy(&src_str, FE::Utf8, dst_opts, FE::Utf8)
1641                    }
1642                    StringEncoding::Utf16 => self.string_utf8_to_utf16(&src_str, dst_opts),
1643                    StringEncoding::CompactUtf16 => {
1644                        self.string_to_compact(&src_str, FE::Utf8, dst_opts)
1645                    }
1646                }
1647            }
1648
1649            StringEncoding::Utf16 => {
1650                self.validate_guest_pointer(
1651                    src_opts,
1652                    &src_str.ptr,
1653                    &AllocSize::DoubleLocal(src_str.len.idx),
1654                    2,
1655                    Trap::StringOutOfBounds,
1656                );
1657                match dst_opts.string_encoding {
1658                    StringEncoding::Utf8 => {
1659                        self.string_deflate_to_utf8(&src_str, FE::Utf16, dst_opts)
1660                    }
1661                    StringEncoding::Utf16 => {
1662                        self.string_copy(&src_str, FE::Utf16, dst_opts, FE::Utf16)
1663                    }
1664                    StringEncoding::CompactUtf16 => {
1665                        self.string_to_compact(&src_str, FE::Utf16, dst_opts)
1666                    }
1667                }
1668            }
1669
1670            StringEncoding::CompactUtf16 => {
1671                // Test the tag big to see if this is a utf16 or a latin1 string
1672                // at runtime...
1673                self.instruction(LocalGet(src_str.len.idx));
1674                self.ptr_uconst(src_mem_opts, UTF16_TAG);
1675                self.ptr_and(src_mem_opts);
1676                self.ptr_if(src_mem_opts, BlockType::Empty);
1677
1678                // In the utf16 block unset the upper bit from the length local
1679                // so further calculations have the right value. Afterwards the
1680                // string transcode proceeds assuming utf16.
1681                self.instruction(LocalGet(src_str.len.idx));
1682                self.ptr_uconst(src_mem_opts, UTF16_TAG);
1683                self.ptr_xor(src_mem_opts);
1684                self.instruction(LocalSet(src_str.len.idx));
1685
1686                // Now that we dynamically know this is utf16 perform a
1687                // validation of the guest's pointer to ensure it's aligned and
1688                // in-bounds.
1689                self.validate_guest_pointer(
1690                    src_opts,
1691                    &src_str.ptr,
1692                    &AllocSize::DoubleLocal(src_str.len.idx),
1693                    2,
1694                    Trap::StringOutOfBounds,
1695                );
1696
1697                let s1 = match dst_opts.string_encoding {
1698                    StringEncoding::Utf8 => {
1699                        self.string_deflate_to_utf8(&src_str, FE::Utf16, dst_opts)
1700                    }
1701                    StringEncoding::Utf16 => {
1702                        self.string_copy(&src_str, FE::Utf16, dst_opts, FE::Utf16)
1703                    }
1704                    StringEncoding::CompactUtf16 => {
1705                        self.string_compact_utf16_to_compact(&src_str, dst_opts)
1706                    }
1707                };
1708
1709                self.instruction(Else);
1710
1711                // Now that we dynamically know this is latin1 perform the
1712                // same validation above, but with a different byte length.
1713                self.validate_guest_pointer(
1714                    src_opts,
1715                    &src_str.ptr,
1716                    &AllocSize::Local(src_str.len.idx),
1717                    2,
1718                    Trap::StringOutOfBounds,
1719                );
1720
1721                // In the latin1 block the `src_len` local is already the number
1722                // of code units, so the string transcoding is all that needs to
1723                // happen.
1724                let s2 = match dst_opts.string_encoding {
1725                    StringEncoding::Utf16 => {
1726                        self.string_copy(&src_str, FE::Latin1, dst_opts, FE::Utf16)
1727                    }
1728                    StringEncoding::Utf8 => {
1729                        self.string_deflate_to_utf8(&src_str, FE::Latin1, dst_opts)
1730                    }
1731                    StringEncoding::CompactUtf16 => {
1732                        self.string_copy(&src_str, FE::Latin1, dst_opts, FE::Latin1)
1733                    }
1734                };
1735                // Set our `s2` generated locals to the `s2` generated locals
1736                // as the resulting pointer of this transcode.
1737                self.instruction(LocalGet(s2.ptr.idx));
1738                self.instruction(LocalSet(s1.ptr.idx));
1739                self.instruction(LocalGet(s2.len.idx));
1740                self.instruction(LocalSet(s1.len.idx));
1741                self.instruction(End);
1742                self.free_temp_local(s2.ptr);
1743                self.free_temp_local(s2.len);
1744                s1
1745            }
1746        };
1747
1748        // Store the ptr/length in the desired destination
1749        match dst {
1750            Destination::Stack(s, _) => {
1751                self.instruction(LocalGet(dst_str.ptr.idx));
1752                self.stack_set(&s[..1], dst_mem_opts.ptr());
1753                self.instruction(LocalGet(dst_str.len.idx));
1754                self.stack_set(&s[1..], dst_mem_opts.ptr());
1755            }
1756            Destination::Memory(mem) => {
1757                self.instruction(LocalGet(mem.addr.idx));
1758                self.instruction(LocalGet(dst_str.ptr.idx));
1759                self.ptr_store(mem);
1760                self.instruction(LocalGet(mem.addr.idx));
1761                self.instruction(LocalGet(dst_str.len.idx));
1762                self.ptr_store(&mem.bump(dst_mem_opts.ptr_size().into()));
1763            }
1764            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1765        }
1766
1767        self.free_temp_local(src_str.ptr);
1768        self.free_temp_local(src_str.len);
1769        self.free_temp_local(dst_str.ptr);
1770        self.free_temp_local(dst_str.len);
1771    }
1772
1773    // Corresponding function for `store_string_copy` in the spec.
1774    //
1775    // This performs a transcoding of the string with a one-pass copy from
1776    // the `src` encoding to the `dst` encoding. This is only possible for
1777    // fixed encodings where the first allocation is guaranteed to be an
1778    // appropriate fit so it's not suitable for all encodings.
1779    //
1780    // Imported host transcoding functions here take the src/dst pointers as
1781    // well as the number of code units in the source (which always matches
1782    // the number of code units in the destination). There is no return
1783    // value from the transcode function since the encoding should always
1784    // work on the first pass.
1785    fn string_copy<'c>(
1786        &mut self,
1787        src: &WasmString<'_>,
1788        src_enc: FE,
1789        dst_opts: &'c Options,
1790        dst_enc: FE,
1791    ) -> WasmString<'c> {
1792        assert!(dst_enc.width() >= src_enc.width());
1793
1794        // Validate the string's length is in-bounds. Note that `dst_enc` is
1795        // specifically used here since it's the larger of the two encodings.
1796        // The code-unit size of the src/dst is going to be the same so this is
1797        // the encoding to validate.
1798        self.validate_string_length(src, dst_enc);
1799
1800        let src_mem_opts = {
1801            match &src.opts.data_model {
1802                DataModel::Gc {} => todo!("CM+GC"),
1803                DataModel::LinearMemory(opts) => opts,
1804            }
1805        };
1806        let dst_mem_opts = {
1807            match &dst_opts.data_model {
1808                DataModel::Gc {} => todo!("CM+GC"),
1809                DataModel::LinearMemory(opts) => opts,
1810            }
1811        };
1812
1813        // Convert the source code units length to the destination byte
1814        // length type.
1815        self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
1816        let dst_len = self.local_tee_new_tmp(dst_mem_opts.ptr());
1817        if dst_enc.width() > 1 {
1818            assert_eq!(dst_enc.width(), 2);
1819            self.ptr_uconst(dst_mem_opts, 1);
1820            self.ptr_shl(dst_mem_opts);
1821        }
1822        let dst_byte_len = self.local_set_new_tmp(dst_mem_opts.ptr());
1823
1824        // Allocate space in the destination using the calculated byte
1825        // length.
1826        let dst = {
1827            let dst_mem = self.malloc(
1828                dst_opts,
1829                AllocSize::Local(dst_byte_len.idx),
1830                dst_enc.align().into(),
1831                Trap::StringOutOfBounds,
1832            );
1833            WasmString {
1834                ptr: dst_mem.addr,
1835                len: dst_len,
1836                opts: dst_opts,
1837            }
1838        };
1839
1840        // If the validations pass then the host `transcode` intrinsic
1841        // is invoked. This will either raise a trap or otherwise succeed
1842        // in which case we're done.
1843        let op = if src_enc == dst_enc {
1844            Transcode::Copy(src_enc)
1845        } else {
1846            assert_eq!(src_enc, FE::Latin1);
1847            assert_eq!(dst_enc, FE::Utf16);
1848            Transcode::Latin1ToUtf16
1849        };
1850        let transcode = self.transcoder(src, &dst, op);
1851        self.instruction(LocalGet(src.ptr.idx));
1852        self.instruction(LocalGet(src.len.idx));
1853        self.instruction(LocalGet(dst.ptr.idx));
1854        self.instruction(Call(transcode.as_u32()));
1855
1856        self.free_temp_local(dst_byte_len);
1857
1858        dst
1859    }
1860
1861    // Corresponding function for `store_string_to_utf8` in the spec.
1862    //
1863    // This translation works by possibly performing a number of
1864    // reallocations. First a buffer of size input-code-units is used to try
1865    // to get the transcoding correct on the first try. If that fails the
1866    // maximum worst-case size is used and then that is resized down if it's
1867    // too large.
1868    //
1869    // The host transcoding function imported here will receive src ptr/len
1870    // and dst ptr/len and return how many code units were consumed on both
1871    // sides. The amount of code units consumed in the source dictates which
1872    // branches are taken in this conversion.
1873    fn string_deflate_to_utf8<'c>(
1874        &mut self,
1875        src: &WasmString<'_>,
1876        src_enc: FE,
1877        dst_opts: &'c Options,
1878    ) -> WasmString<'c> {
1879        let src_mem_opts = match &src.opts.data_model {
1880            DataModel::Gc {} => todo!("CM+GC"),
1881            DataModel::LinearMemory(opts) => opts,
1882        };
1883        let dst_mem_opts = match &dst_opts.data_model {
1884            DataModel::Gc {} => todo!("CM+GC"),
1885            DataModel::LinearMemory(opts) => opts,
1886        };
1887
1888        self.validate_string_length(src, src_enc);
1889
1890        // Optimistically assume that the code unit length of the source is
1891        // all that's needed in the destination. Perform that allocation
1892        // here and proceed to transcoding below.
1893        self.convert_src_len_to_dst(
1894            src.len.idx,
1895            src.opts.data_model.unwrap_memory().ptr(),
1896            dst_opts.data_model.unwrap_memory().ptr(),
1897        );
1898        let dst_len = self.local_tee_new_tmp(dst_opts.data_model.unwrap_memory().ptr());
1899        let dst_byte_len = self.local_set_new_tmp(dst_opts.data_model.unwrap_memory().ptr());
1900
1901        let dst = {
1902            let dst_mem = self.malloc(
1903                dst_opts,
1904                AllocSize::Local(dst_byte_len.idx),
1905                1,
1906                Trap::StringOutOfBounds,
1907            );
1908            WasmString {
1909                ptr: dst_mem.addr,
1910                len: dst_len,
1911                opts: dst_opts,
1912            }
1913        };
1914
1915        // Perform the initial transcode
1916        let op = match src_enc {
1917            FE::Latin1 => Transcode::Latin1ToUtf8,
1918            FE::Utf16 => Transcode::Utf16ToUtf8,
1919            FE::Utf8 => unreachable!(),
1920        };
1921        let transcode = self.transcoder(src, &dst, op);
1922        self.instruction(LocalGet(src.ptr.idx));
1923        self.instruction(LocalGet(src.len.idx));
1924        self.instruction(LocalGet(dst.ptr.idx));
1925        self.instruction(LocalGet(dst_byte_len.idx));
1926        self.instruction(I32Const(1)); // first_pass = true
1927        self.instruction(Call(transcode.as_u32()));
1928        self.instruction(LocalSet(dst.len.idx));
1929        let src_len_tmp = self.local_set_new_tmp(src.opts.data_model.unwrap_memory().ptr());
1930
1931        // Test if the source was entirely transcoded by comparing
1932        // `src_len_tmp`, the number of code units transcoded from the
1933        // source, with `src_len`, the original number of code units.
1934        self.instruction(LocalGet(src_len_tmp.idx));
1935        self.instruction(LocalGet(src.len.idx));
1936        self.ptr_ne(src_mem_opts);
1937        self.instruction(If(BlockType::Empty));
1938
1939        // Check that the worst-case byte size fits within the maximum size of
1940        // strings.
1941        let factor = match src_enc {
1942            FE::Latin1 => 2,
1943            FE::Utf16 => 3,
1944            _ => unreachable!(),
1945        };
1946        self.validate_string_length_u8(src, factor);
1947        self.convert_src_len_to_dst(
1948            src.len.idx,
1949            src.opts.data_model.unwrap_memory().ptr(),
1950            dst_opts.data_model.unwrap_memory().ptr(),
1951        );
1952        self.ptr_uconst(dst_mem_opts, factor.into());
1953        self.ptr_mul(dst_mem_opts);
1954        let new_byte_len = self.local_set_new_tmp(dst_mem_opts.ptr());
1955
1956        // Do a worst-case reallocation is performed to grow `dst_mem`.
1957        // Afterwards update our `dst_byte_len` local to reflect the new byte
1958        // length.
1959        self.realloc(
1960            dst_opts,
1961            &dst.ptr,
1962            AllocSize::Local(dst_byte_len.idx),
1963            AllocSize::Local(new_byte_len.idx),
1964            1,
1965            Trap::StringOutOfBounds,
1966        );
1967        self.instruction(LocalGet(new_byte_len.idx));
1968        self.instruction(LocalSet(dst_byte_len.idx));
1969        self.free_temp_local(new_byte_len);
1970
1971        // Perform another round of transcoding that should be guaranteed
1972        // to succeed. Note that all the parameters here are offset by the
1973        // results of the first transcoding to only perform the remaining
1974        // transcode on the final units.
1975        self.instruction(LocalGet(src.ptr.idx));
1976        self.instruction(LocalGet(src_len_tmp.idx));
1977        if let FE::Utf16 = src_enc {
1978            self.ptr_uconst(src_mem_opts, 1);
1979            self.ptr_shl(src_mem_opts);
1980        }
1981        self.ptr_add(src_mem_opts);
1982        self.instruction(LocalGet(src.len.idx));
1983        self.instruction(LocalGet(src_len_tmp.idx));
1984        self.ptr_sub(src_mem_opts);
1985        self.instruction(LocalGet(dst.ptr.idx));
1986        self.instruction(LocalGet(dst.len.idx));
1987        self.ptr_add(dst_mem_opts);
1988        self.instruction(LocalGet(dst_byte_len.idx));
1989        self.instruction(LocalGet(dst.len.idx));
1990        self.ptr_sub(dst_mem_opts);
1991        self.instruction(I32Const(0)); // first_pass = false
1992        self.instruction(Call(transcode.as_u32()));
1993
1994        // Add the second result, the amount of destination units encoded,
1995        // to `dst_len` so it's an accurate reflection of the final size of
1996        // the destination buffer.
1997        self.instruction(LocalGet(dst.len.idx));
1998        self.ptr_add(dst_mem_opts);
1999        self.instruction(LocalSet(dst.len.idx));
2000
2001        // In debug mode verify the first result consumed the entire string,
2002        // otherwise simply discard it.
2003        if self.module.tunables.debug_adapter_modules {
2004            self.instruction(LocalGet(src.len.idx));
2005            self.instruction(LocalGet(src_len_tmp.idx));
2006            self.ptr_sub(src_mem_opts);
2007            self.ptr_ne(src_mem_opts);
2008            self.instruction(If(BlockType::Empty));
2009            self.trap(Trap::DebugAssertStringEncodingFinished);
2010            self.instruction(End);
2011        } else {
2012            self.instruction(Drop);
2013        }
2014
2015        // Perform a downsizing if the worst-case size was too large
2016        self.instruction(LocalGet(dst.len.idx));
2017        self.instruction(LocalGet(dst_byte_len.idx));
2018        self.ptr_ne(dst_mem_opts);
2019        self.instruction(If(BlockType::Empty));
2020        self.realloc(
2021            dst_opts,
2022            &dst.ptr,
2023            AllocSize::Local(dst_byte_len.idx),
2024            AllocSize::Local(dst.len.idx),
2025            1,
2026            Trap::StringOutOfBounds,
2027        );
2028        self.instruction(End);
2029
2030        // If the first transcode was enough then assert that the returned
2031        // amount of destination items written equals the byte size.
2032        if self.module.tunables.debug_adapter_modules {
2033            self.instruction(Else);
2034
2035            self.instruction(LocalGet(dst.len.idx));
2036            self.instruction(LocalGet(dst_byte_len.idx));
2037            self.ptr_ne(dst_mem_opts);
2038            self.instruction(If(BlockType::Empty));
2039            self.trap(Trap::DebugAssertStringEncodingFinished);
2040            self.instruction(End);
2041        }
2042
2043        self.instruction(End); // end of "first transcode not enough"
2044
2045        self.free_temp_local(src_len_tmp);
2046        self.free_temp_local(dst_byte_len);
2047
2048        dst
2049    }
2050
2051    // Corresponds to the `store_utf8_to_utf16` function in the spec.
2052    //
2053    // When converting utf-8 to utf-16 a pessimistic allocation is
2054    // done which is twice the byte length of the utf-8 string.
2055    // The host then transcodes and returns how many code units were
2056    // actually used during the transcoding and if it's beneath the
2057    // pessimistic maximum then the buffer is reallocated down to
2058    // a smaller amount.
2059    //
2060    // The host-imported transcoding function takes the src/dst pointer as
2061    // well as the code unit size of both the source and destination. The
2062    // destination should always be big enough to hold the result of the
2063    // transcode and so the result of the host function is how many code
2064    // units were written to the destination.
2065    fn string_utf8_to_utf16<'c>(
2066        &mut self,
2067        src: &WasmString<'_>,
2068        dst_opts: &'c Options,
2069    ) -> WasmString<'c> {
2070        let src_mem_opts = match &src.opts.data_model {
2071            DataModel::Gc {} => todo!("CM+GC"),
2072            DataModel::LinearMemory(opts) => opts,
2073        };
2074        let dst_mem_opts = match &dst_opts.data_model {
2075            DataModel::Gc {} => todo!("CM+GC"),
2076            DataModel::LinearMemory(opts) => opts,
2077        };
2078
2079        self.validate_string_length(src, FE::Utf16);
2080        self.convert_src_len_to_dst(
2081            src.len.idx,
2082            src_mem_opts.ptr(),
2083            dst_opts.data_model.unwrap_memory().ptr(),
2084        );
2085        let dst_len = self.local_tee_new_tmp(dst_opts.data_model.unwrap_memory().ptr());
2086        self.ptr_uconst(dst_mem_opts, 1);
2087        self.ptr_shl(dst_mem_opts);
2088        let dst_byte_len = self.local_set_new_tmp(dst_opts.data_model.unwrap_memory().ptr());
2089        let dst = {
2090            let dst_mem = self.malloc(
2091                dst_opts,
2092                AllocSize::Local(dst_byte_len.idx),
2093                2,
2094                Trap::StringOutOfBounds,
2095            );
2096            WasmString {
2097                ptr: dst_mem.addr,
2098                len: dst_len,
2099                opts: dst_opts,
2100            }
2101        };
2102
2103        let transcode = self.transcoder(src, &dst, Transcode::Utf8ToUtf16);
2104        self.instruction(LocalGet(src.ptr.idx));
2105        self.instruction(LocalGet(src.len.idx));
2106        self.instruction(LocalGet(dst.ptr.idx));
2107        self.instruction(Call(transcode.as_u32()));
2108        self.instruction(LocalSet(dst.len.idx));
2109
2110        // If the number of code units returned by transcode is not
2111        // equal to the original number of code units then
2112        // the buffer must be shrunk.
2113        //
2114        // Note that the byte length of the final allocation we
2115        // want is twice the code unit length returned by the
2116        // transcoding function.
2117        self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2118        self.instruction(LocalGet(dst.len.idx));
2119        self.ptr_ne(dst_mem_opts);
2120        self.instruction(If(BlockType::Empty));
2121        self.realloc(
2122            dst.opts,
2123            &dst.ptr,
2124            AllocSize::Local(dst_byte_len.idx),
2125            AllocSize::DoubleLocal(dst.len.idx),
2126            2,
2127            Trap::StringOutOfBounds,
2128        );
2129        self.instruction(End); // end of shrink-to-fit
2130
2131        self.free_temp_local(dst_byte_len);
2132
2133        dst
2134    }
2135
2136    // Corresponds to `store_probably_utf16_to_latin1_or_utf16` in the spec.
2137    //
2138    // This will try to transcode the input utf16 string to utf16 in the
2139    // destination. If utf16 isn't needed though and latin1 could be used
2140    // then that's used instead and a reallocation to downsize occurs
2141    // afterwards.
2142    //
2143    // The host transcode function here will take the src/dst pointers as
2144    // well as src length. The destination byte length is twice the src code
2145    // unit length. The return value is the tagged length of the returned
2146    // string. If the upper bit is set then utf16 was used and the
2147    // conversion is done. If the upper bit is not set then latin1 was used
2148    // and a downsizing needs to happen.
2149    fn string_compact_utf16_to_compact<'c>(
2150        &mut self,
2151        src: &WasmString<'_>,
2152        dst_opts: &'c Options,
2153    ) -> WasmString<'c> {
2154        let src_mem_opts = match &src.opts.data_model {
2155            DataModel::Gc {} => todo!("CM+GC"),
2156            DataModel::LinearMemory(opts) => opts,
2157        };
2158        let dst_mem_opts = match &dst_opts.data_model {
2159            DataModel::Gc {} => todo!("CM+GC"),
2160            DataModel::LinearMemory(opts) => opts,
2161        };
2162
2163        self.validate_string_length(src, FE::Utf16);
2164        self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2165        let dst_len = self.local_tee_new_tmp(dst_mem_opts.ptr());
2166        self.ptr_uconst(dst_mem_opts, 1);
2167        self.ptr_shl(dst_mem_opts);
2168        let dst_byte_len = self.local_set_new_tmp(dst_mem_opts.ptr());
2169        let dst = {
2170            let dst_mem = self.malloc(
2171                dst_opts,
2172                AllocSize::Local(dst_byte_len.idx),
2173                2,
2174                Trap::StringOutOfBounds,
2175            );
2176            WasmString {
2177                ptr: dst_mem.addr,
2178                len: dst_len,
2179                opts: dst_opts,
2180            }
2181        };
2182
2183        self.convert_src_len_to_dst(
2184            dst_byte_len.idx,
2185            dst.opts.data_model.unwrap_memory().ptr(),
2186            src_mem_opts.ptr(),
2187        );
2188        let src_byte_len = self.local_set_new_tmp(src_mem_opts.ptr());
2189
2190        let transcode = self.transcoder(src, &dst, Transcode::Utf16ToCompactProbablyUtf16);
2191        self.instruction(LocalGet(src.ptr.idx));
2192        self.instruction(LocalGet(src.len.idx));
2193        self.instruction(LocalGet(dst.ptr.idx));
2194        self.instruction(Call(transcode.as_u32()));
2195        self.instruction(LocalSet(dst.len.idx));
2196
2197        // Assert that the untagged code unit length is the same as the
2198        // source code unit length.
2199        if self.module.tunables.debug_adapter_modules {
2200            self.instruction(LocalGet(dst.len.idx));
2201            self.ptr_uconst(dst_mem_opts, !UTF16_TAG);
2202            self.ptr_and(dst_mem_opts);
2203            self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2204            self.ptr_ne(dst_mem_opts);
2205            self.instruction(If(BlockType::Empty));
2206            self.trap(Trap::DebugAssertEqualCodeUnits);
2207            self.instruction(End);
2208        }
2209
2210        // If the UTF16_TAG is set then utf16 was used and the destination
2211        // should be appropriately sized. Bail out of the "is this string
2212        // empty" block and fall through otherwise to resizing.
2213        self.instruction(LocalGet(dst.len.idx));
2214        self.ptr_uconst(dst_mem_opts, UTF16_TAG);
2215        self.ptr_and(dst_mem_opts);
2216        self.ptr_br_if(dst_mem_opts, 0);
2217
2218        // Here `realloc` is used to downsize the string
2219        self.realloc(
2220            dst.opts,
2221            &dst.ptr,
2222            AllocSize::Local(dst_byte_len.idx),
2223            AllocSize::Local(dst.len.idx),
2224            2,
2225            Trap::StringOutOfBounds,
2226        );
2227
2228        self.free_temp_local(dst_byte_len);
2229        self.free_temp_local(src_byte_len);
2230
2231        dst
2232    }
2233
2234    // Corresponds to `store_string_to_latin1_or_utf16` in the spec.
2235    //
2236    // This will attempt a first pass of transcoding to latin1 and on
2237    // failure a larger buffer is allocated for utf16 and then utf16 is
2238    // encoded in-place into the buffer. After either latin1 or utf16 the
2239    // buffer is then resized to fit the final string allocation.
2240    fn string_to_compact<'c>(
2241        &mut self,
2242        src: &WasmString<'_>,
2243        src_enc: FE,
2244        dst_opts: &'c Options,
2245    ) -> WasmString<'c> {
2246        let src_mem_opts = match &src.opts.data_model {
2247            DataModel::Gc {} => todo!("CM+GC"),
2248            DataModel::LinearMemory(opts) => opts,
2249        };
2250        let dst_mem_opts = match &dst_opts.data_model {
2251            DataModel::Gc {} => todo!("CM+GC"),
2252            DataModel::LinearMemory(opts) => opts,
2253        };
2254
2255        self.validate_string_length(src, src_enc);
2256
2257        self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2258        let dst_len = self.local_tee_new_tmp(dst_mem_opts.ptr());
2259        let dst_byte_len = self.local_set_new_tmp(dst_mem_opts.ptr());
2260        let dst = {
2261            let dst_mem = self.malloc(
2262                dst_opts,
2263                AllocSize::Local(dst_byte_len.idx),
2264                2,
2265                Trap::StringOutOfBounds,
2266            );
2267            WasmString {
2268                ptr: dst_mem.addr,
2269                len: dst_len,
2270                opts: dst_opts,
2271            }
2272        };
2273
2274        // Perform the initial latin1 transcode. This returns the number of
2275        // source code units consumed and the number of destination code
2276        // units (bytes) written.
2277        let (latin1, utf16) = match src_enc {
2278            FE::Utf8 => (Transcode::Utf8ToLatin1, Transcode::Utf8ToCompactUtf16),
2279            FE::Utf16 => (Transcode::Utf16ToLatin1, Transcode::Utf16ToCompactUtf16),
2280            FE::Latin1 => unreachable!(),
2281        };
2282        let transcode_latin1 = self.transcoder(src, &dst, latin1);
2283        let transcode_utf16 = self.transcoder(src, &dst, utf16);
2284        self.instruction(LocalGet(src.ptr.idx));
2285        self.instruction(LocalGet(src.len.idx));
2286        self.instruction(LocalGet(dst.ptr.idx));
2287        self.instruction(Call(transcode_latin1.as_u32()));
2288        self.instruction(LocalSet(dst.len.idx));
2289        let src_len_tmp = self.local_set_new_tmp(src_mem_opts.ptr());
2290
2291        // If the source was entirely consumed then the transcode completed
2292        // and all that's necessary is to optionally shrink the buffer.
2293        self.instruction(LocalGet(src_len_tmp.idx));
2294        self.instruction(LocalGet(src.len.idx));
2295        self.ptr_eq(src_mem_opts);
2296        self.instruction(If(BlockType::Empty)); // if latin1-or-utf16 block
2297
2298        // Test if the original byte length of the allocation is the same as
2299        // the number of written bytes, and if not then shrink the buffer
2300        // with a call to `realloc`.
2301        self.instruction(LocalGet(dst_byte_len.idx));
2302        self.instruction(LocalGet(dst.len.idx));
2303        self.ptr_ne(dst_mem_opts);
2304        self.instruction(If(BlockType::Empty));
2305        self.realloc(
2306            dst.opts,
2307            &dst.ptr,
2308            AllocSize::Local(dst_byte_len.idx),
2309            AllocSize::Local(dst.len.idx),
2310            2,
2311            Trap::StringOutOfBounds,
2312        );
2313        self.instruction(End);
2314
2315        // In this block the latin1 encoding failed. The host transcode
2316        // returned how many units were consumed from the source and how
2317        // many bytes were written to the destination. Here the buffer is
2318        // inflated and sized and the second utf16 intrinsic is invoked to
2319        // perform the final inflation.
2320        self.instruction(Else); // else latin1-or-utf16 block
2321
2322        // For utf8 validate that the inflated size is still within bounds.
2323        if src_enc.width() == 1 {
2324            self.validate_string_length_u8(src, 2);
2325        }
2326
2327        // Reallocate the buffer with twice the source code units in byte
2328        // size.
2329        self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2330        self.ptr_uconst(dst_mem_opts, 1);
2331        self.ptr_shl(dst_mem_opts);
2332        let new_byte_len = self.local_set_new_tmp(dst_mem_opts.ptr());
2333        self.realloc(
2334            dst.opts,
2335            &dst.ptr,
2336            AllocSize::Local(dst_byte_len.idx),
2337            AllocSize::Local(new_byte_len.idx),
2338            2,
2339            Trap::StringOutOfBounds,
2340        );
2341        self.instruction(LocalGet(new_byte_len.idx));
2342        self.instruction(LocalSet(dst_byte_len.idx));
2343        self.free_temp_local(new_byte_len);
2344
2345        // Call the host utf16 transcoding function. This will inflate the
2346        // prior latin1 bytes and then encode the rest of the source string
2347        // as utf16 into the remaining space in the destination buffer.
2348        self.instruction(LocalGet(src.ptr.idx));
2349        self.instruction(LocalGet(src_len_tmp.idx));
2350        if let FE::Utf16 = src_enc {
2351            self.ptr_uconst(src_mem_opts, 1);
2352            self.ptr_shl(src_mem_opts);
2353        }
2354        self.ptr_add(src_mem_opts);
2355        self.instruction(LocalGet(src.len.idx));
2356        self.instruction(LocalGet(src_len_tmp.idx));
2357        self.ptr_sub(src_mem_opts);
2358        self.instruction(LocalGet(dst.ptr.idx));
2359        self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2360        self.instruction(LocalGet(dst.len.idx));
2361        self.instruction(Call(transcode_utf16.as_u32()));
2362        self.instruction(LocalSet(dst.len.idx));
2363
2364        // If the returned number of code units written to the destination
2365        // is not equal to the size of the allocation then the allocation is
2366        // resized down to the appropriate size.
2367        //
2368        // Note that the byte size desired is `2*dst_len` and the current
2369        // byte buffer size is `2*src_len` so the `2` factor isn't checked
2370        // here, just the lengths.
2371        self.instruction(LocalGet(dst.len.idx));
2372        self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2373        self.ptr_ne(dst_mem_opts);
2374        self.instruction(If(BlockType::Empty));
2375        self.realloc(
2376            dst.opts,
2377            &dst.ptr,
2378            AllocSize::Local(dst_byte_len.idx),
2379            AllocSize::DoubleLocal(dst.len.idx),
2380            2,
2381            Trap::StringOutOfBounds,
2382        );
2383        self.instruction(End);
2384
2385        // Tag the returned pointer as utf16
2386        self.instruction(LocalGet(dst.len.idx));
2387        self.ptr_uconst(dst_mem_opts, UTF16_TAG);
2388        self.ptr_or(dst_mem_opts);
2389        self.instruction(LocalSet(dst.len.idx));
2390
2391        self.instruction(End); // end latin1-or-utf16 block
2392
2393        self.free_temp_local(src_len_tmp);
2394        self.free_temp_local(dst_byte_len);
2395
2396        dst
2397    }
2398
2399    fn validate_string_length(&mut self, src: &WasmString<'_>, dst: FE) {
2400        self.validate_string_length_u8(src, dst.width())
2401    }
2402
2403    fn validate_string_length_u8(&mut self, s: &WasmString<'_>, dst: u8) {
2404        let mem_opts = match &s.opts.data_model {
2405            DataModel::Gc {} => todo!("CM+GC"),
2406            DataModel::LinearMemory(opts) => opts,
2407        };
2408
2409        // Check to see if the source byte length is out of bounds in
2410        // which case a trap is generated.
2411        self.instruction(LocalGet(s.len.idx));
2412        let max = MAX_STRING_BYTE_LENGTH / u32::from(dst);
2413        self.ptr_uconst(mem_opts, max);
2414        self.ptr_gt_u(mem_opts);
2415        self.instruction(If(BlockType::Empty));
2416        self.trap(Trap::StringOutOfBounds);
2417        self.instruction(End);
2418    }
2419
2420    fn transcoder(
2421        &mut self,
2422        src: &WasmString<'_>,
2423        dst: &WasmString<'_>,
2424        op: Transcode,
2425    ) -> FuncIndex {
2426        match (src.opts.data_model, dst.opts.data_model) {
2427            (DataModel::Gc {}, _) | (_, DataModel::Gc {}) => {
2428                todo!("CM+GC")
2429            }
2430            (
2431                DataModel::LinearMemory(LinearMemoryOptions {
2432                    memory: Some((src_mem, src_ty)),
2433                    realloc: _,
2434                }),
2435                DataModel::LinearMemory(LinearMemoryOptions {
2436                    memory: Some((dst_mem, dst_ty)),
2437                    realloc: _,
2438                }),
2439            ) => self.module.import_transcoder(Transcoder {
2440                from_memory: src_mem,
2441                from_memory64: src_ty.idx_type == IndexType::I64,
2442                to_memory: dst_mem,
2443                to_memory64: dst_ty.idx_type == IndexType::I64,
2444                op,
2445            }),
2446            (DataModel::LinearMemory(LinearMemoryOptions { memory: None, .. }), _)
2447            | (_, DataModel::LinearMemory(LinearMemoryOptions { memory: None, .. })) => {
2448                unreachable!()
2449            }
2450        }
2451    }
2452
2453    /// Shared preamble for translating list-like sequences (lists and maps).
2454    ///
2455    /// Emits: load ptr/len from source, compute byte lengths, malloc
2456    /// destination, validate bounds, and if element sizes are non-zero opens
2457    /// Block + Loop and initializes iteration locals.
2458    ///
2459    /// Returns a `SequenceTranslation` that the caller uses to emit the
2460    /// loop body before calling `end_translate_sequence`.
2461    fn begin_translate_sequence<'c>(
2462        &mut self,
2463        src: &Source<'c>,
2464        dst: &Destination<'c>,
2465        src_element_size: u32,
2466        src_element_align: u32,
2467        dst_element_size: u32,
2468        dst_element_align: u32,
2469    ) -> SequenceTranslation<'c> {
2470        let src_mem_opts = match &src.opts().data_model {
2471            DataModel::Gc {} => todo!("CM+GC"),
2472            DataModel::LinearMemory(opts) => opts,
2473        };
2474        let dst_mem_opts = match &dst.opts().data_model {
2475            DataModel::Gc {} => todo!("CM+GC"),
2476            DataModel::LinearMemory(opts) => opts,
2477        };
2478
2479        let src_opts = src.opts();
2480        let dst_opts = dst.opts();
2481
2482        // Load the pointer/length of this sequence into temporary locals.
2483        // These will be referenced a good deal so this just makes it easier
2484        // to deal with them consistently below rather than trying to reload
2485        // from memory for example.
2486        match src {
2487            Source::Stack(s) => {
2488                assert_eq!(s.locals.len(), 2);
2489                self.stack_get(&s.slice(0..1), src_mem_opts.ptr());
2490                self.stack_get(&s.slice(1..2), src_mem_opts.ptr());
2491            }
2492            Source::Memory(mem) => {
2493                self.ptr_load(mem);
2494                self.ptr_load(&mem.bump(src_mem_opts.ptr_size().into()));
2495            }
2496            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
2497        }
2498        let src_len = self.local_set_new_tmp(src_mem_opts.ptr());
2499        let src_ptr = self.local_set_new_tmp(src_mem_opts.ptr());
2500
2501        // Calculate the source/destination byte lengths into unique locals.
2502        let src_byte_len =
2503            self.calculate_list_byte_len(src_mem_opts, src_len.idx, src_element_size);
2504        let dst_byte_len = if src_element_size == dst_element_size {
2505            self.convert_src_len_to_dst(src_byte_len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2506            self.local_set_new_tmp(dst_mem_opts.ptr())
2507        } else if src_mem_opts.ptr() == dst_mem_opts.ptr() {
2508            self.calculate_list_byte_len(dst_mem_opts, src_len.idx, dst_element_size)
2509        } else {
2510            self.convert_src_len_to_dst(src_len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2511            let tmp = self.local_set_new_tmp(dst_mem_opts.ptr());
2512            let ret = self.calculate_list_byte_len(dst_mem_opts, tmp.idx, dst_element_size);
2513            self.free_temp_local(tmp);
2514            ret
2515        };
2516
2517        // Create a `Memory` operand which will internally assert that the
2518        // `src_ptr` value is properly aligned.
2519        let src_mem = self.memory_operand(
2520            src_opts,
2521            src_ptr,
2522            AllocSize::Local(src_byte_len.idx),
2523            src_element_align,
2524            Trap::ListOutOfBounds,
2525        );
2526
2527        // Here `realloc` is invoked (in a `malloc`-like fashion) to allocate
2528        // space for the sequence in the destination memory. This will also
2529        // internally insert checks that the returned pointer is aligned
2530        // correctly for the destination.
2531        let dst_mem = self.malloc(
2532            dst_opts,
2533            AllocSize::Local(dst_byte_len.idx),
2534            dst_element_align,
2535            Trap::ListOutOfBounds,
2536        );
2537
2538        self.free_temp_local(src_byte_len);
2539        self.free_temp_local(dst_byte_len);
2540
2541        // If both element sizes are 0 then there's nothing to copy so the
2542        // loop is skipped entirely. Otherwise open a Block (for early exit
2543        // on zero-length) and a Loop for the per-element iteration.
2544        let loop_state = if src_element_size > 0 || dst_element_size > 0 {
2545            self.instruction(Block(BlockType::Empty));
2546
2547            // Set the `remaining` local and only continue if it's > 0.
2548            self.instruction(LocalGet(src_len.idx));
2549            let remaining = self.local_tee_new_tmp(src_mem_opts.ptr());
2550            self.ptr_eqz(src_mem_opts);
2551            self.instruction(BrIf(0));
2552
2553            // Initialize the two iteration pointers to their starting values.
2554            self.instruction(LocalGet(src_mem.addr.idx));
2555            let cur_src_ptr = self.local_set_new_tmp(src_mem_opts.ptr());
2556            self.instruction(LocalGet(dst_mem.addr.idx));
2557            let cur_dst_ptr = self.local_set_new_tmp(dst_mem_opts.ptr());
2558
2559            self.instruction(Loop(BlockType::Empty));
2560
2561            Some(SequenceLoopState {
2562                remaining,
2563                cur_src_ptr,
2564                cur_dst_ptr,
2565            })
2566        } else {
2567            None
2568        };
2569
2570        SequenceTranslation {
2571            src_len,
2572            src_mem,
2573            dst_mem,
2574            src_opts,
2575            dst_opts,
2576            src_mem_opts,
2577            dst_mem_opts,
2578            loop_state,
2579        }
2580    }
2581
2582    /// Shared epilogue for translating list-like sequences.
2583    ///
2584    /// If a loop was opened, emits: decrement remaining, BrIf to loop
2585    /// head, End loop, End block, and frees iteration locals. Then stores
2586    /// the ptr/len pair into the destination and frees all temporaries.
2587    fn end_translate_sequence(&mut self, seq: SequenceTranslation<'_>, dst: &Destination) {
2588        if let Some(loop_state) = seq.loop_state {
2589            // Update the remaining count, falling through to break out if
2590            // it's zero now.
2591            self.instruction(LocalGet(loop_state.remaining.idx));
2592            self.ptr_iconst(seq.src_mem_opts, -1);
2593            self.ptr_add(seq.src_mem_opts);
2594            self.instruction(LocalTee(loop_state.remaining.idx));
2595            self.ptr_br_if(seq.src_mem_opts, 0);
2596            self.instruction(End); // end of loop
2597            self.instruction(End); // end of block
2598
2599            self.free_temp_local(loop_state.cur_dst_ptr);
2600            self.free_temp_local(loop_state.cur_src_ptr);
2601            self.free_temp_local(loop_state.remaining);
2602        }
2603
2604        // Store the ptr/length in the desired destination.
2605        match dst {
2606            Destination::Stack(s, _) => {
2607                self.instruction(LocalGet(seq.dst_mem.addr.idx));
2608                self.stack_set(&s[..1], seq.dst_mem_opts.ptr());
2609                self.convert_src_len_to_dst(
2610                    seq.src_len.idx,
2611                    seq.src_mem_opts.ptr(),
2612                    seq.dst_mem_opts.ptr(),
2613                );
2614                self.stack_set(&s[1..], seq.dst_mem_opts.ptr());
2615            }
2616            Destination::Memory(mem) => {
2617                self.instruction(LocalGet(mem.addr.idx));
2618                self.instruction(LocalGet(seq.dst_mem.addr.idx));
2619                self.ptr_store(mem);
2620                self.instruction(LocalGet(mem.addr.idx));
2621                self.convert_src_len_to_dst(
2622                    seq.src_len.idx,
2623                    seq.src_mem_opts.ptr(),
2624                    seq.dst_mem_opts.ptr(),
2625                );
2626                self.ptr_store(&mem.bump(seq.dst_mem_opts.ptr_size().into()));
2627            }
2628            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
2629        }
2630
2631        self.free_temp_local(seq.src_len);
2632        self.free_temp_local(seq.src_mem.addr);
2633        self.free_temp_local(seq.dst_mem.addr);
2634    }
2635
2636    fn translate_list(
2637        &mut self,
2638        src_ty: TypeListIndex,
2639        src: &Source<'_>,
2640        dst_ty: &InterfaceType,
2641        dst: &Destination,
2642    ) {
2643        let src_mem_opts = match &src.opts().data_model {
2644            DataModel::Gc {} => todo!("CM+GC"),
2645            DataModel::LinearMemory(opts) => opts,
2646        };
2647        let dst_mem_opts = match &dst.opts().data_model {
2648            DataModel::Gc {} => todo!("CM+GC"),
2649            DataModel::LinearMemory(opts) => opts,
2650        };
2651
2652        let src_element_ty = &self.types[src_ty].element;
2653        let dst_element_ty = match dst_ty {
2654            InterfaceType::List(r) => &self.types[*r].element,
2655            _ => panic!("expected a list"),
2656        };
2657        let (src_size, src_align) = self.types.size_align(src_mem_opts, src_element_ty);
2658        let (dst_size, dst_align) = self.types.size_align(dst_mem_opts, dst_element_ty);
2659
2660        let seq = self.begin_translate_sequence(src, dst, src_size, src_align, dst_size, dst_align);
2661
2662        if let Some(ref loop_state) = seq.loop_state {
2663            let element_src = Source::Memory(Memory {
2664                opts: seq.src_opts,
2665                offset: 0,
2666                addr: TempLocal::new(loop_state.cur_src_ptr.idx, loop_state.cur_src_ptr.ty),
2667            });
2668            let element_dst = Destination::Memory(Memory {
2669                opts: seq.dst_opts,
2670                offset: 0,
2671                addr: TempLocal::new(loop_state.cur_dst_ptr.idx, loop_state.cur_dst_ptr.ty),
2672            });
2673            self.translate(src_element_ty, &element_src, dst_element_ty, &element_dst);
2674
2675            if src_size > 0 {
2676                self.instruction(LocalGet(loop_state.cur_src_ptr.idx));
2677                self.ptr_uconst(src_mem_opts, src_size);
2678                self.ptr_add(src_mem_opts);
2679                self.instruction(LocalSet(loop_state.cur_src_ptr.idx));
2680            }
2681            if dst_size > 0 {
2682                self.instruction(LocalGet(loop_state.cur_dst_ptr.idx));
2683                self.ptr_uconst(dst_mem_opts, dst_size);
2684                self.ptr_add(dst_mem_opts);
2685                self.instruction(LocalSet(loop_state.cur_dst_ptr.idx));
2686            }
2687        }
2688
2689        self.end_translate_sequence(seq, dst);
2690    }
2691
2692    /// Translates a map from one component's memory to another.
2693    ///
2694    /// In the Component Model, a `map<K, V>` is stored in memory as
2695    /// `list<tuple<K, V>>`, so the translation reuses the same sequence
2696    /// scaffolding as lists but with a two-field (key, value) loop body.
2697    fn translate_map(
2698        &mut self,
2699        src_ty: TypeMapIndex,
2700        src: &Source<'_>,
2701        dst_ty: &InterfaceType,
2702        dst: &Destination,
2703    ) {
2704        let src_mem_opts = match &src.opts().data_model {
2705            DataModel::Gc {} => todo!("CM+GC"),
2706            DataModel::LinearMemory(opts) => opts,
2707        };
2708        let dst_mem_opts = match &dst.opts().data_model {
2709            DataModel::Gc {} => todo!("CM+GC"),
2710            DataModel::LinearMemory(opts) => opts,
2711        };
2712
2713        let src_map_ty = &self.types[src_ty];
2714        let dst_map_ty = match dst_ty {
2715            InterfaceType::Map(r) => &self.types[*r],
2716            _ => panic!("expected a map"),
2717        };
2718
2719        // Each map entry is a tuple<K, V> following record layout rules.
2720        let src_key_abi = self.types.canonical_abi(&src_map_ty.key);
2721        let src_value_abi = self.types.canonical_abi(&src_map_ty.value);
2722        let src_entry_abi = CanonicalAbiInfo::record([src_key_abi, src_value_abi].into_iter());
2723        let (src_tuple_size, src_entry_align) = src_mem_opts.sizealign(&src_entry_abi);
2724        let src_value_offset = {
2725            let mut offset = 0u32;
2726            if src_mem_opts.memory64() {
2727                src_key_abi.next_field64(&mut offset);
2728                src_value_abi.next_field64(&mut offset)
2729            } else {
2730                src_key_abi.next_field32(&mut offset);
2731                src_value_abi.next_field32(&mut offset)
2732            }
2733        };
2734
2735        let dst_key_abi = self.types.canonical_abi(&dst_map_ty.key);
2736        let dst_value_abi = self.types.canonical_abi(&dst_map_ty.value);
2737        let dst_entry_abi = CanonicalAbiInfo::record([dst_key_abi, dst_value_abi].into_iter());
2738        let (dst_tuple_size, dst_entry_align) = dst_mem_opts.sizealign(&dst_entry_abi);
2739        let dst_value_offset = {
2740            let mut offset = 0u32;
2741            if dst_mem_opts.memory64() {
2742                dst_key_abi.next_field64(&mut offset);
2743                dst_value_abi.next_field64(&mut offset)
2744            } else {
2745                dst_key_abi.next_field32(&mut offset);
2746                dst_value_abi.next_field32(&mut offset)
2747            }
2748        };
2749
2750        let seq = self.begin_translate_sequence(
2751            src,
2752            dst,
2753            src_tuple_size,
2754            src_entry_align,
2755            dst_tuple_size,
2756            dst_entry_align,
2757        );
2758
2759        if let Some(ref loop_state) = seq.loop_state {
2760            let key_src = Source::Memory(Memory {
2761                opts: seq.src_opts,
2762                offset: 0,
2763                addr: TempLocal::new(loop_state.cur_src_ptr.idx, src_mem_opts.ptr()),
2764            });
2765            let key_dst = Destination::Memory(Memory {
2766                opts: seq.dst_opts,
2767                offset: 0,
2768                addr: TempLocal::new(loop_state.cur_dst_ptr.idx, dst_mem_opts.ptr()),
2769            });
2770            self.translate(&src_map_ty.key, &key_src, &dst_map_ty.key, &key_dst);
2771
2772            let value_src = Source::Memory(Memory {
2773                opts: seq.src_opts,
2774                offset: src_value_offset,
2775                addr: TempLocal::new(loop_state.cur_src_ptr.idx, src_mem_opts.ptr()),
2776            });
2777            let value_dst = Destination::Memory(Memory {
2778                opts: seq.dst_opts,
2779                offset: dst_value_offset,
2780                addr: TempLocal::new(loop_state.cur_dst_ptr.idx, dst_mem_opts.ptr()),
2781            });
2782            self.translate(&src_map_ty.value, &value_src, &dst_map_ty.value, &value_dst);
2783
2784            // Advance past value + trailing padding to the next entry
2785            if src_tuple_size > 0 {
2786                self.instruction(LocalGet(loop_state.cur_src_ptr.idx));
2787                self.ptr_uconst(src_mem_opts, src_tuple_size);
2788                self.ptr_add(src_mem_opts);
2789                self.instruction(LocalSet(loop_state.cur_src_ptr.idx));
2790            }
2791            if dst_tuple_size > 0 {
2792                self.instruction(LocalGet(loop_state.cur_dst_ptr.idx));
2793                self.ptr_uconst(dst_mem_opts, dst_tuple_size);
2794                self.ptr_add(dst_mem_opts);
2795                self.instruction(LocalSet(loop_state.cur_dst_ptr.idx));
2796            }
2797        }
2798
2799        self.end_translate_sequence(seq, dst);
2800    }
2801
2802    fn calculate_list_byte_len(
2803        &mut self,
2804        opts: &LinearMemoryOptions,
2805        len_local: u32,
2806        elt_size: u32,
2807    ) -> TempLocal {
2808        // Zero-size types are easy to handle here because the byte size of the
2809        // destination is always zero.
2810        if elt_size == 0 {
2811            self.ptr_uconst(opts, 0);
2812            return self.local_set_new_tmp(opts.ptr());
2813        }
2814
2815        // For one-byte elements in the destination the check here can be a bit
2816        // more optimal than the general case below. In these situations if the
2817        // source pointer type is 32-bit then we're guaranteed to not overflow,
2818        // so the source length is simply casted to the destination's type.
2819        //
2820        // If the source is 64-bit then all that needs to be checked is to
2821        // ensure that it does not have the upper 32-bits set.
2822        if elt_size == 1 {
2823            if let ValType::I64 = opts.ptr() {
2824                self.instruction(LocalGet(len_local));
2825                self.instruction(I64Const(32));
2826                self.instruction(I64ShrU);
2827                self.instruction(I32WrapI64);
2828                self.instruction(If(BlockType::Empty));
2829                self.trap(Trap::ListOutOfBounds);
2830                self.instruction(End);
2831            }
2832            self.instruction(LocalGet(len_local));
2833            return self.local_set_new_tmp(opts.ptr());
2834        }
2835
2836        // The main check implemented by this function is to verify that
2837        // `src_len_local` does not exceed the 32-bit range. Byte sizes for
2838        // lists must always fit in 32-bits to get transferred to 32-bit
2839        // memories.
2840        self.instruction(Block(BlockType::Empty));
2841        self.instruction(Block(BlockType::Empty));
2842        self.instruction(LocalGet(len_local));
2843        match opts.ptr() {
2844            // The source's list length is guaranteed to be less than 32-bits
2845            // so simply extend it up to a 64-bit type for the multiplication
2846            // below.
2847            ValType::I32 => self.instruction(I64ExtendI32U),
2848
2849            // If the source is a 64-bit memory then if the item length doesn't
2850            // fit in 32-bits the byte length definitely won't, so generate a
2851            // branch to our overflow trap here if any of the upper 32-bits are set.
2852            ValType::I64 => {
2853                self.instruction(I64Const(32));
2854                self.instruction(I64ShrU);
2855                self.instruction(I32WrapI64);
2856                self.instruction(BrIf(0));
2857                self.instruction(LocalGet(len_local));
2858            }
2859
2860            _ => unreachable!(),
2861        }
2862
2863        // Next perform a 64-bit multiplication with the element byte size that
2864        // is itself guaranteed to fit in 32-bits. The result is then checked
2865        // to see if we overflowed the 32-bit space. The two input operands to
2866        // the multiplication are guaranteed to be 32-bits at most which means
2867        // that this multiplication shouldn't overflow.
2868        //
2869        // The result of the multiplication is saved into a local as well to
2870        // get the result afterwards.
2871        self.instruction(I64Const(elt_size.into()));
2872        self.instruction(I64Mul);
2873        let tmp = self.local_tee_new_tmp(ValType::I64);
2874        // Branch to success if the upper 32-bits are zero, otherwise
2875        // fall-through to the trap.
2876        self.instruction(I64Const(32));
2877        self.instruction(I64ShrU);
2878        self.instruction(I64Eqz);
2879        self.instruction(BrIf(1));
2880        self.instruction(End);
2881        self.trap(Trap::ListOutOfBounds);
2882        self.instruction(End);
2883
2884        // If a fresh local was used to store the result of the multiplication
2885        // then convert it down to 32-bits which should be guaranteed to not
2886        // lose information at this point.
2887        if opts.ptr() == ValType::I64 {
2888            tmp
2889        } else {
2890            self.instruction(LocalGet(tmp.idx));
2891            self.instruction(I32WrapI64);
2892            self.free_temp_local(tmp);
2893            self.local_set_new_tmp(ValType::I32)
2894        }
2895    }
2896
2897    fn convert_src_len_to_dst(
2898        &mut self,
2899        src_len_local: u32,
2900        src_ptr_ty: ValType,
2901        dst_ptr_ty: ValType,
2902    ) {
2903        self.instruction(LocalGet(src_len_local));
2904        match (src_ptr_ty, dst_ptr_ty) {
2905            (ValType::I32, ValType::I64) => self.instruction(I64ExtendI32U),
2906            (ValType::I64, ValType::I32) => self.instruction(I32WrapI64),
2907            (src, dst) => assert_eq!(src, dst),
2908        }
2909    }
2910
2911    fn translate_record(
2912        &mut self,
2913        src_ty: TypeRecordIndex,
2914        src: &Source<'_>,
2915        dst_ty: &InterfaceType,
2916        dst: &Destination,
2917    ) {
2918        let src_ty = &self.types[src_ty];
2919        let dst_ty = match dst_ty {
2920            InterfaceType::Record(r) => &self.types[*r],
2921            _ => panic!("expected a record"),
2922        };
2923
2924        // TODO: subtyping
2925        assert_eq!(src_ty.fields.len(), dst_ty.fields.len());
2926
2927        // First a map is made of the source fields to where they're coming
2928        // from (e.g. which offset or which locals). This map is keyed by the
2929        // fields' names
2930        let mut src_fields = HashMap::new();
2931        for (i, src) in src
2932            .record_field_srcs(self.types, src_ty.fields.iter().map(|f| f.ty))
2933            .enumerate()
2934        {
2935            let field = &src_ty.fields[i];
2936            src_fields.insert(&field.name, (src, &field.ty));
2937        }
2938
2939        // .. and next translation is performed in the order of the destination
2940        // fields in case the destination is the stack to ensure that the stack
2941        // has the fields all in the right order.
2942        //
2943        // Note that the lookup in `src_fields` is an infallible lookup which
2944        // will panic if the field isn't found.
2945        //
2946        // TODO: should that lookup be fallible with subtyping?
2947        for (i, dst) in dst
2948            .record_field_dsts(self.types, dst_ty.fields.iter().map(|f| f.ty))
2949            .enumerate()
2950        {
2951            let field = &dst_ty.fields[i];
2952            let (src, src_ty) = &src_fields[&field.name];
2953            self.translate(src_ty, src, &field.ty, &dst);
2954        }
2955    }
2956
2957    fn translate_flags(
2958        &mut self,
2959        src_ty: TypeFlagsIndex,
2960        src: &Source<'_>,
2961        dst_ty: &InterfaceType,
2962        dst: &Destination,
2963    ) {
2964        let src_ty = &self.types[src_ty];
2965        let dst_ty = match dst_ty {
2966            InterfaceType::Flags(r) => &self.types[*r],
2967            _ => panic!("expected a record"),
2968        };
2969
2970        // TODO: subtyping
2971        //
2972        // Notably this implementation does not support reordering flags from
2973        // the source to the destination nor having more flags in the
2974        // destination. Currently this is a copy from source to destination
2975        // in-bulk. Otherwise reordering indices would have to have some sort of
2976        // fancy bit twiddling tricks or something like that.
2977        assert_eq!(src_ty.names, dst_ty.names);
2978        let cnt = src_ty.names.len();
2979        match FlagsSize::from_count(cnt) {
2980            FlagsSize::Size0 => {}
2981            FlagsSize::Size1 => {
2982                let mask = if cnt == 8 { 0xff } else { (1 << cnt) - 1 };
2983                self.convert_u8_mask(src, dst, mask);
2984            }
2985            FlagsSize::Size2 => {
2986                let mask = if cnt == 16 { 0xffff } else { (1 << cnt) - 1 };
2987                self.convert_u16_mask(src, dst, mask);
2988            }
2989            FlagsSize::Size4Plus(n) => {
2990                let srcs = src.record_field_srcs(self.types, (0..n).map(|_| InterfaceType::U32));
2991                let dsts = dst.record_field_dsts(self.types, (0..n).map(|_| InterfaceType::U32));
2992                let n = usize::from(n);
2993                for (i, (src, dst)) in srcs.zip(dsts).enumerate() {
2994                    let mask = if i == n - 1 && (cnt % 32 != 0) {
2995                        (1 << (cnt % 32)) - 1
2996                    } else {
2997                        0xffffffff
2998                    };
2999                    self.convert_u32_mask(&src, &dst, mask);
3000                }
3001            }
3002        }
3003    }
3004
3005    fn translate_tuple(
3006        &mut self,
3007        src_ty: TypeTupleIndex,
3008        src: &Source<'_>,
3009        dst_ty: &InterfaceType,
3010        dst: &Destination,
3011    ) {
3012        let src_ty = &self.types[src_ty];
3013        let dst_ty = match dst_ty {
3014            InterfaceType::Tuple(t) => &self.types[*t],
3015            _ => panic!("expected a tuple"),
3016        };
3017
3018        // TODO: subtyping
3019        assert_eq!(src_ty.types.len(), dst_ty.types.len());
3020
3021        let srcs = src
3022            .record_field_srcs(self.types, src_ty.types.iter().copied())
3023            .zip(src_ty.types.iter());
3024        let dsts = dst
3025            .record_field_dsts(self.types, dst_ty.types.iter().copied())
3026            .zip(dst_ty.types.iter());
3027        for ((src, src_ty), (dst, dst_ty)) in srcs.zip(dsts) {
3028            self.translate(src_ty, &src, dst_ty, &dst);
3029        }
3030    }
3031
3032    fn translate_fixed_length_list(
3033        &mut self,
3034        src_ty: TypeFixedLengthListIndex,
3035        src: &Source<'_>,
3036        dst_ty: &InterfaceType,
3037        dst: &Destination,
3038    ) {
3039        let src_ty = &self.types[src_ty];
3040        let dst_ty = match dst_ty {
3041            InterfaceType::FixedLengthList(t) => &self.types[*t],
3042            _ => panic!("expected a fixed size list"),
3043        };
3044
3045        // TODO: subtyping
3046        assert_eq!(src_ty.size, dst_ty.size);
3047
3048        match (&src, &dst) {
3049            // Generate custom code for memory to memory copy
3050            (Source::Memory(src_mem), Destination::Memory(dst_mem)) => {
3051                let src_mem_opts = match &src_mem.opts.data_model {
3052                    DataModel::Gc {} => todo!("CM+GC"),
3053                    DataModel::LinearMemory(opts) => opts,
3054                };
3055                let dst_mem_opts = match &dst_mem.opts.data_model {
3056                    DataModel::Gc {} => todo!("CM+GC"),
3057                    DataModel::LinearMemory(opts) => opts,
3058                };
3059                let src_element_bytes = self.types.size_align(src_mem_opts, &src_ty.element).0;
3060                let dst_element_bytes = self.types.size_align(dst_mem_opts, &dst_ty.element).0;
3061                assert_ne!(src_element_bytes, 0);
3062                assert_ne!(dst_element_bytes, 0);
3063
3064                // because data is stored in-line, we assume that source and destination memory have been validated upstream
3065
3066                self.instruction(LocalGet(src_mem.addr.idx));
3067                if src_mem.offset != 0 {
3068                    self.ptr_uconst(src_mem_opts, src_mem.offset);
3069                    self.ptr_add(src_mem_opts);
3070                }
3071                let cur_src_ptr = self.local_set_new_tmp(src_mem_opts.ptr());
3072                self.instruction(LocalGet(dst_mem.addr.idx));
3073                if dst_mem.offset != 0 {
3074                    self.ptr_uconst(dst_mem_opts, dst_mem.offset);
3075                    self.ptr_add(dst_mem_opts);
3076                }
3077                let cur_dst_ptr = self.local_set_new_tmp(dst_mem_opts.ptr());
3078
3079                self.instruction(I32Const(src_ty.size as i32));
3080                let remaining = self.local_set_new_tmp(ValType::I32);
3081
3082                self.instruction(Loop(BlockType::Empty));
3083
3084                // Translate the next element in the list
3085                let element_src = Source::Memory(Memory {
3086                    opts: src_mem.opts,
3087                    offset: 0,
3088                    addr: TempLocal::new(cur_src_ptr.idx, cur_src_ptr.ty),
3089                });
3090                let element_dst = Destination::Memory(Memory {
3091                    opts: dst_mem.opts,
3092                    offset: 0,
3093                    addr: TempLocal::new(cur_dst_ptr.idx, cur_dst_ptr.ty),
3094                });
3095                self.translate(&src_ty.element, &element_src, &dst_ty.element, &element_dst);
3096
3097                // Update the two loop pointers
3098                self.instruction(LocalGet(cur_src_ptr.idx));
3099                self.ptr_uconst(src_mem_opts, src_element_bytes);
3100                self.ptr_add(src_mem_opts);
3101                self.instruction(LocalSet(cur_src_ptr.idx));
3102                self.instruction(LocalGet(cur_dst_ptr.idx));
3103                self.ptr_uconst(dst_mem_opts, dst_element_bytes);
3104                self.ptr_add(dst_mem_opts);
3105                self.instruction(LocalSet(cur_dst_ptr.idx));
3106
3107                // Update the remaining count, falling through to break out if it's zero
3108                // now.
3109                self.instruction(LocalGet(remaining.idx));
3110                self.ptr_iconst(src_mem_opts, -1);
3111                self.ptr_add(src_mem_opts);
3112                self.instruction(LocalTee(remaining.idx));
3113                self.ptr_br_if(src_mem_opts, 0);
3114                self.instruction(End); // end of loop
3115
3116                self.free_temp_local(cur_dst_ptr);
3117                self.free_temp_local(cur_src_ptr);
3118                self.free_temp_local(remaining);
3119                return;
3120            }
3121            // for the non-memory-to-memory case fall back to using generic tuple translation
3122            (_, _) => {
3123                // Assumes that the number of elements are small enough for this unrolling
3124                assert!(
3125                    src_ty.size as usize <= MAX_FLAT_PARAMS
3126                        && dst_ty.size as usize <= MAX_FLAT_PARAMS
3127                );
3128                let srcs =
3129                    src.record_field_srcs(self.types, (0..src_ty.size).map(|_| src_ty.element));
3130                let dsts =
3131                    dst.record_field_dsts(self.types, (0..dst_ty.size).map(|_| dst_ty.element));
3132                for (src, dst) in srcs.zip(dsts) {
3133                    self.translate(&src_ty.element, &src, &dst_ty.element, &dst);
3134                }
3135            }
3136        }
3137    }
3138
3139    fn translate_variant(
3140        &mut self,
3141        src_ty: TypeVariantIndex,
3142        src: &Source<'_>,
3143        dst_ty: &InterfaceType,
3144        dst: &Destination,
3145    ) {
3146        let src_ty = &self.types[src_ty];
3147        let dst_ty = match dst_ty {
3148            InterfaceType::Variant(t) => &self.types[*t],
3149            _ => panic!("expected a variant"),
3150        };
3151
3152        let src_info = variant_info(self.types, src_ty.cases.iter().map(|(_, c)| c.as_ref()));
3153        let dst_info = variant_info(self.types, dst_ty.cases.iter().map(|(_, c)| c.as_ref()));
3154
3155        let iter = src_ty
3156            .cases
3157            .iter()
3158            .enumerate()
3159            .map(|(src_i, (src_case, src_case_ty))| {
3160                let dst_i = dst_ty
3161                    .cases
3162                    .iter()
3163                    .position(|(c, _)| c == src_case)
3164                    .unwrap();
3165                let dst_case_ty = &dst_ty.cases[dst_i];
3166                let src_i = u32::try_from(src_i).unwrap();
3167                let dst_i = u32::try_from(dst_i).unwrap();
3168                VariantCase {
3169                    src_i,
3170                    src_ty: src_case_ty.as_ref(),
3171                    dst_i,
3172                    dst_ty: dst_case_ty.as_ref(),
3173                }
3174            });
3175        self.convert_variant(src, &src_info, dst, &dst_info, iter);
3176    }
3177
3178    fn translate_enum(
3179        &mut self,
3180        src_ty: TypeEnumIndex,
3181        src: &Source<'_>,
3182        dst_ty: &InterfaceType,
3183        dst: &Destination,
3184    ) {
3185        let src_ty = &self.types[src_ty];
3186        let dst_ty = match dst_ty {
3187            InterfaceType::Enum(t) => &self.types[*t],
3188            _ => panic!("expected an option"),
3189        };
3190
3191        debug_assert_eq!(src_ty.info.size, dst_ty.info.size);
3192        debug_assert_eq!(src_ty.names.len(), dst_ty.names.len());
3193        debug_assert!(
3194            src_ty
3195                .names
3196                .iter()
3197                .zip(dst_ty.names.iter())
3198                .all(|(a, b)| a == b)
3199        );
3200
3201        // Get the discriminant.
3202        match src {
3203            Source::Stack(s) => self.stack_get(&s.slice(0..1), ValType::I32),
3204            Source::Memory(mem) => match src_ty.info.size {
3205                DiscriminantSize::Size1 => self.i32_load8u(mem),
3206                DiscriminantSize::Size2 => self.i32_load16u(mem),
3207                DiscriminantSize::Size4 => self.i32_load(mem),
3208            },
3209            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
3210        }
3211        let tmp = self.local_tee_new_tmp(ValType::I32);
3212
3213        // Assert that the discriminant is valid.
3214        self.instruction(I32Const(i32::try_from(src_ty.names.len()).unwrap()));
3215        self.instruction(I32GeU);
3216        self.instruction(If(BlockType::Empty));
3217        self.trap(Trap::InvalidDiscriminant);
3218        self.instruction(End);
3219
3220        // Save the discriminant to the destination.
3221        match dst {
3222            Destination::Stack(stack, _) => {
3223                self.local_get_tmp(&tmp);
3224                self.stack_set(&stack[..1], ValType::I32)
3225            }
3226            Destination::Memory(mem) => {
3227                self.push_dst_addr(dst);
3228                self.local_get_tmp(&tmp);
3229                match dst_ty.info.size {
3230                    DiscriminantSize::Size1 => self.i32_store8(mem),
3231                    DiscriminantSize::Size2 => self.i32_store16(mem),
3232                    DiscriminantSize::Size4 => self.i32_store(mem),
3233                }
3234            }
3235            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
3236        }
3237        self.free_temp_local(tmp);
3238    }
3239
3240    fn translate_option(
3241        &mut self,
3242        src_ty: TypeOptionIndex,
3243        src: &Source<'_>,
3244        dst_ty: &InterfaceType,
3245        dst: &Destination,
3246    ) {
3247        let src_ty = &self.types[src_ty].ty;
3248        let dst_ty = match dst_ty {
3249            InterfaceType::Option(t) => &self.types[*t].ty,
3250            _ => panic!("expected an option"),
3251        };
3252        let src_ty = Some(src_ty);
3253        let dst_ty = Some(dst_ty);
3254
3255        let src_info = variant_info(self.types, [None, src_ty]);
3256        let dst_info = variant_info(self.types, [None, dst_ty]);
3257
3258        self.convert_variant(
3259            src,
3260            &src_info,
3261            dst,
3262            &dst_info,
3263            [
3264                VariantCase {
3265                    src_i: 0,
3266                    dst_i: 0,
3267                    src_ty: None,
3268                    dst_ty: None,
3269                },
3270                VariantCase {
3271                    src_i: 1,
3272                    dst_i: 1,
3273                    src_ty,
3274                    dst_ty,
3275                },
3276            ]
3277            .into_iter(),
3278        );
3279    }
3280
3281    fn translate_result(
3282        &mut self,
3283        src_ty: TypeResultIndex,
3284        src: &Source<'_>,
3285        dst_ty: &InterfaceType,
3286        dst: &Destination,
3287    ) {
3288        let src_ty = &self.types[src_ty];
3289        let dst_ty = match dst_ty {
3290            InterfaceType::Result(t) => &self.types[*t],
3291            _ => panic!("expected a result"),
3292        };
3293
3294        let src_info = variant_info(self.types, [src_ty.ok.as_ref(), src_ty.err.as_ref()]);
3295        let dst_info = variant_info(self.types, [dst_ty.ok.as_ref(), dst_ty.err.as_ref()]);
3296
3297        self.convert_variant(
3298            src,
3299            &src_info,
3300            dst,
3301            &dst_info,
3302            [
3303                VariantCase {
3304                    src_i: 0,
3305                    dst_i: 0,
3306                    src_ty: src_ty.ok.as_ref(),
3307                    dst_ty: dst_ty.ok.as_ref(),
3308                },
3309                VariantCase {
3310                    src_i: 1,
3311                    dst_i: 1,
3312                    src_ty: src_ty.err.as_ref(),
3313                    dst_ty: dst_ty.err.as_ref(),
3314                },
3315            ]
3316            .into_iter(),
3317        );
3318    }
3319
3320    fn convert_variant<'c>(
3321        &mut self,
3322        src: &Source<'_>,
3323        src_info: &VariantInfo,
3324        dst: &Destination,
3325        dst_info: &VariantInfo,
3326        src_cases: impl ExactSizeIterator<Item = VariantCase<'c>>,
3327    ) {
3328        // The outermost block is special since it has the result type of the
3329        // translation here. That will depend on the `dst`.
3330        let outer_block_ty = match dst {
3331            Destination::Stack(dst_flat, _) => match dst_flat.len() {
3332                0 => BlockType::Empty,
3333                1 => BlockType::Result(dst_flat[0]),
3334                _ => {
3335                    let ty = self.module.core_types.function(&[], &dst_flat);
3336                    BlockType::FunctionType(ty)
3337                }
3338            },
3339            Destination::Memory(_) => BlockType::Empty,
3340            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
3341        };
3342        self.instruction(Block(outer_block_ty));
3343
3344        // After the outermost block generate a new block for each of the
3345        // remaining cases.
3346        let src_cases_len = src_cases.len();
3347        for _ in 0..src_cases_len - 1 {
3348            self.instruction(Block(BlockType::Empty));
3349        }
3350
3351        // Generate a block for an invalid variant discriminant
3352        self.instruction(Block(BlockType::Empty));
3353
3354        // And generate one final block that we'll be jumping out of with the
3355        // `br_table`
3356        self.instruction(Block(BlockType::Empty));
3357
3358        // Load the discriminant
3359        match src {
3360            Source::Stack(s) => self.stack_get(&s.slice(0..1), ValType::I32),
3361            Source::Memory(mem) => match src_info.size {
3362                DiscriminantSize::Size1 => self.i32_load8u(mem),
3363                DiscriminantSize::Size2 => self.i32_load16u(mem),
3364                DiscriminantSize::Size4 => self.i32_load(mem),
3365            },
3366            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
3367        }
3368
3369        // Generate the `br_table` for the discriminant. Each case has an
3370        // offset of 1 to skip the trapping block.
3371        let mut targets = Vec::new();
3372        for i in 0..src_cases_len {
3373            targets.push((i + 1) as u32);
3374        }
3375        self.instruction(BrTable(targets[..].into(), 0));
3376        self.instruction(End); // end the `br_table` block
3377
3378        self.trap(Trap::InvalidDiscriminant);
3379        self.instruction(End); // end the "invalid discriminant" block
3380
3381        // Translate each case individually within its own block. Note that the
3382        // iteration order here places the first case in the innermost block
3383        // and the last case in the outermost block. This matches the order
3384        // of the jump targets in the `br_table` instruction.
3385        let src_cases_len = u32::try_from(src_cases_len).unwrap();
3386        for case in src_cases {
3387            let VariantCase {
3388                src_i,
3389                src_ty,
3390                dst_i,
3391                dst_ty,
3392            } = case;
3393
3394            // Translate the discriminant here, noting that `dst_i` may be
3395            // different than `src_i`.
3396            self.push_dst_addr(dst);
3397            self.instruction(I32Const(dst_i as i32));
3398            match dst {
3399                Destination::Stack(stack, _) => self.stack_set(&stack[..1], ValType::I32),
3400                Destination::Memory(mem) => match dst_info.size {
3401                    DiscriminantSize::Size1 => self.i32_store8(mem),
3402                    DiscriminantSize::Size2 => self.i32_store16(mem),
3403                    DiscriminantSize::Size4 => self.i32_store(mem),
3404                },
3405                Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
3406            }
3407
3408            let src_payload = src.payload_src(self.types, src_info, src_ty);
3409            let dst_payload = dst.payload_dst(self.types, dst_info, dst_ty);
3410
3411            // Translate the payload of this case using the various types from
3412            // the dst/src.
3413            match (src_ty, dst_ty) {
3414                (Some(src_ty), Some(dst_ty)) => {
3415                    self.translate(src_ty, &src_payload, dst_ty, &dst_payload);
3416                }
3417                (None, None) => {}
3418                _ => unimplemented!(),
3419            }
3420
3421            // If the results of this translation were placed on the stack then
3422            // the stack values may need to be padded with more zeros due to
3423            // this particular case being possibly smaller than the entire
3424            // variant. That's handled here by pushing remaining zeros after
3425            // accounting for the discriminant pushed as well as the results of
3426            // this individual payload.
3427            if let Destination::Stack(payload_results, _) = dst_payload {
3428                if let Destination::Stack(dst_results, _) = dst {
3429                    let remaining = &dst_results[1..][payload_results.len()..];
3430                    for ty in remaining {
3431                        match ty {
3432                            ValType::I32 => self.instruction(I32Const(0)),
3433                            ValType::I64 => self.instruction(I64Const(0)),
3434                            ValType::F32 => self.instruction(F32Const(0.0.into())),
3435                            ValType::F64 => self.instruction(F64Const(0.0.into())),
3436                            _ => unreachable!(),
3437                        }
3438                    }
3439                }
3440            }
3441
3442            // Branch to the outermost block. Note that this isn't needed for
3443            // the outermost case since it simply falls through.
3444            if src_i != src_cases_len - 1 {
3445                self.instruction(Br(src_cases_len - src_i - 1));
3446            }
3447            self.instruction(End); // end this case's block
3448        }
3449    }
3450
3451    fn translate_future(
3452        &mut self,
3453        src_ty: TypeFutureTableIndex,
3454        src: &Source<'_>,
3455        dst_ty: &InterfaceType,
3456        dst: &Destination,
3457    ) {
3458        let dst_ty = match dst_ty {
3459            InterfaceType::Future(t) => *t,
3460            _ => panic!("expected a `Future`"),
3461        };
3462        let transfer = self.module.import_future_transfer();
3463        self.translate_handle(src_ty.as_u32(), src, dst_ty.as_u32(), dst, transfer);
3464    }
3465
3466    fn translate_stream(
3467        &mut self,
3468        src_ty: TypeStreamTableIndex,
3469        src: &Source<'_>,
3470        dst_ty: &InterfaceType,
3471        dst: &Destination,
3472    ) {
3473        let dst_ty = match dst_ty {
3474            InterfaceType::Stream(t) => *t,
3475            _ => panic!("expected a `Stream`"),
3476        };
3477        let transfer = self.module.import_stream_transfer();
3478        self.translate_handle(src_ty.as_u32(), src, dst_ty.as_u32(), dst, transfer);
3479    }
3480
3481    fn translate_error_context(
3482        &mut self,
3483        src_ty: TypeComponentLocalErrorContextTableIndex,
3484        src: &Source<'_>,
3485        dst_ty: &InterfaceType,
3486        dst: &Destination,
3487    ) {
3488        let dst_ty = match dst_ty {
3489            InterfaceType::ErrorContext(t) => *t,
3490            _ => panic!("expected an `ErrorContext`"),
3491        };
3492        let transfer = self.module.import_error_context_transfer();
3493        self.translate_handle(src_ty.as_u32(), src, dst_ty.as_u32(), dst, transfer);
3494    }
3495
3496    fn translate_own(
3497        &mut self,
3498        src_ty: TypeResourceTableIndex,
3499        src: &Source<'_>,
3500        dst_ty: &InterfaceType,
3501        dst: &Destination,
3502    ) {
3503        let dst_ty = match dst_ty {
3504            InterfaceType::Own(t) => *t,
3505            _ => panic!("expected an `Own`"),
3506        };
3507        let transfer = self.module.import_resource_transfer_own();
3508        self.translate_handle(src_ty.as_u32(), src, dst_ty.as_u32(), dst, transfer);
3509    }
3510
3511    fn translate_borrow(
3512        &mut self,
3513        src_ty: TypeResourceTableIndex,
3514        src: &Source<'_>,
3515        dst_ty: &InterfaceType,
3516        dst: &Destination,
3517    ) {
3518        let dst_ty = match dst_ty {
3519            InterfaceType::Borrow(t) => *t,
3520            _ => panic!("expected an `Borrow`"),
3521        };
3522
3523        let transfer = self.module.import_resource_transfer_borrow();
3524        self.translate_handle(src_ty.as_u32(), src, dst_ty.as_u32(), dst, transfer);
3525    }
3526
3527    /// Translates the index `src`, which resides in the table `src_ty`, into
3528    /// and index within `dst_ty` and is stored at `dst`.
3529    ///
3530    /// Actual translation of the index happens in a wasmtime libcall, which a
3531    /// cranelift-generated trampoline to satisfy this import will call. The
3532    /// `transfer` function is an imported function which takes the src, src_ty,
3533    /// and dst_ty, and returns the dst index.
3534    fn translate_handle(
3535        &mut self,
3536        src_ty: u32,
3537        src: &Source<'_>,
3538        dst_ty: u32,
3539        dst: &Destination,
3540        transfer: FuncIndex,
3541    ) {
3542        self.push_dst_addr(dst);
3543        match src {
3544            Source::Memory(mem) => self.i32_load(mem),
3545            Source::Stack(stack) => self.stack_get(stack, ValType::I32),
3546            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
3547        }
3548        self.instruction(I32Const(src_ty as i32));
3549        self.instruction(I32Const(dst_ty as i32));
3550        self.instruction(Call(transfer.as_u32()));
3551        match dst {
3552            Destination::Memory(mem) => self.i32_store(mem),
3553            Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
3554            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
3555        }
3556    }
3557
3558    fn trap_if_not_flag(&mut self, flags_global: GlobalIndex, flag_to_test: i32, trap: Trap) {
3559        self.instruction(GlobalGet(flags_global.as_u32()));
3560        self.instruction(I32Const(flag_to_test));
3561        self.instruction(I32And);
3562        self.instruction(I32Eqz);
3563        self.instruction(If(BlockType::Empty));
3564        self.trap(trap);
3565        self.instruction(End);
3566    }
3567
3568    fn set_flag(&mut self, flags_global: GlobalIndex, flag_to_set: i32, value: bool) {
3569        self.instruction(GlobalGet(flags_global.as_u32()));
3570        if value {
3571            self.instruction(I32Const(flag_to_set));
3572            self.instruction(I32Or);
3573        } else {
3574            self.instruction(I32Const(!flag_to_set));
3575            self.instruction(I32And);
3576        }
3577        self.instruction(GlobalSet(flags_global.as_u32()));
3578    }
3579
3580    fn assert_aligned(&mut self, ty: &InterfaceType, mem: &Memory) {
3581        let mem_opts = mem.mem_opts();
3582        if !self.module.tunables.debug_adapter_modules {
3583            return;
3584        }
3585        let align = self.types.align(mem_opts, ty);
3586        if align == 1 {
3587            return;
3588        }
3589        assert!(align.is_power_of_two());
3590        self.instruction(LocalGet(mem.addr.idx));
3591        self.ptr_uconst(mem_opts, mem.offset);
3592        self.ptr_add(mem_opts);
3593        self.ptr_uconst(mem_opts, align - 1);
3594        self.ptr_and(mem_opts);
3595        self.ptr_if(mem_opts, BlockType::Empty);
3596        self.trap(Trap::DebugAssertPointerAligned);
3597        self.instruction(End);
3598    }
3599
3600    /// Helper to invoke the guest's `realloc` function with a statically known
3601    /// `abi`.
3602    ///
3603    /// This will internally validate the return value is properly aligned and
3604    /// additionally within bounds of memory.
3605    fn malloc_abi<'c>(
3606        &mut self,
3607        opts: &'c Options,
3608        abi: &CanonicalAbiInfo,
3609        oob_trap: Trap,
3610    ) -> Memory<'c> {
3611        match &opts.data_model {
3612            DataModel::Gc {} => todo!("CM+GC"),
3613            DataModel::LinearMemory(mem_opts) => {
3614                let (size, align) = mem_opts.sizealign(abi);
3615                let size = AllocSize::Const(size);
3616                self.malloc(opts, size, align, oob_trap)
3617            }
3618        }
3619    }
3620
3621    /// Helper to invoke the guest's `realloc` function with the specified
3622    /// `size` and `align`.
3623    ///
3624    /// This will internally validate the return value is properly aligned and
3625    /// additionally within bounds of memory.
3626    fn malloc<'c>(
3627        &mut self,
3628        opts: &'c Options,
3629        size: AllocSize,
3630        align: u32,
3631        oob_trap: Trap,
3632    ) -> Memory<'c> {
3633        match &opts.data_model {
3634            DataModel::Gc {} => todo!("CM+GC"),
3635            DataModel::LinearMemory(mem_opts) => {
3636                let realloc = mem_opts.realloc.unwrap();
3637                self.ptr_uconst(mem_opts, 0);
3638                self.ptr_uconst(mem_opts, 0);
3639                self.ptr_uconst(mem_opts, align);
3640                self.alloc_size(mem_opts, &size);
3641                self.instruction(Call(realloc.as_u32()));
3642                let addr = self.local_set_new_tmp(mem_opts.ptr());
3643                self.memory_operand(opts, addr, size, align, oob_trap)
3644            }
3645        }
3646    }
3647
3648    /// Helper to invoke the guest's `realloc` function with the specified
3649    /// arguments.
3650    ///
3651    /// This will internally validate the return value is properly aligned and
3652    /// additionally within bounds of memory.
3653    fn realloc(
3654        &mut self,
3655        opts: &Options,
3656        ptr: &TempLocal,
3657        prev_size: AllocSize,
3658        size: AllocSize,
3659        align: u32,
3660        oob_trap: Trap,
3661    ) {
3662        match &opts.data_model {
3663            DataModel::Gc {} => todo!("CM+GC"),
3664            DataModel::LinearMemory(mem_opts) => {
3665                let realloc = mem_opts.realloc.unwrap();
3666                self.instruction(LocalGet(ptr.idx));
3667                self.alloc_size(mem_opts, &prev_size);
3668                self.ptr_uconst(mem_opts, align);
3669                self.alloc_size(mem_opts, &size);
3670                self.instruction(Call(realloc.as_u32()));
3671                self.instruction(LocalSet(ptr.idx));
3672                self.validate_guest_pointer(opts, &ptr, &size, align, oob_trap)
3673            }
3674        }
3675    }
3676
3677    /// Convenience helper aruond `memory_operand` which takes a
3678    /// statically known `abi` of the allocation.
3679    fn memory_operand_abi<'c>(
3680        &mut self,
3681        opts: &'c Options,
3682        addr: TempLocal,
3683        abi: &CanonicalAbiInfo,
3684        oob_trap: Trap,
3685    ) -> Memory<'c> {
3686        match &opts.data_model {
3687            DataModel::Gc {} => todo!("CM+GC"),
3688            DataModel::LinearMemory(mem_opts) => {
3689                let (size, align) = mem_opts.sizealign(abi);
3690                self.memory_operand(opts, addr, AllocSize::Const(size), align, oob_trap)
3691            }
3692        }
3693    }
3694
3695    /// Creates a `Memory` operand from the parts provided after validating
3696    /// that everything is in-bounds according to `validate_guest_pointer`.
3697    fn memory_operand<'c>(
3698        &mut self,
3699        opts: &'c Options,
3700        addr: TempLocal,
3701        size: AllocSize,
3702        align: u32,
3703        oob_trap: Trap,
3704    ) -> Memory<'c> {
3705        self.validate_guest_pointer(opts, &addr, &size, align, oob_trap);
3706        Memory {
3707            addr,
3708            opts,
3709            offset: 0,
3710        }
3711    }
3712
3713    /// Validates that the guest pointer `addr` is in-bounds for `size` amount
3714    /// of bytes.
3715    ///
3716    /// Additionally validates that `addr` is aligned to `align`.
3717    ///
3718    /// Traps with `oob_trap` if the `addr` value is not in-bounds for the
3719    /// linear memory specified by `opts`.
3720    fn validate_guest_pointer(
3721        &mut self,
3722        opts: &Options,
3723        addr: &TempLocal,
3724        size: &AllocSize,
3725        align: u32,
3726        oob_trap: Trap,
3727    ) {
3728        let mem_opts = match &opts.data_model {
3729            DataModel::Gc {} => todo!("CM+GC"),
3730            DataModel::LinearMemory(mem_opts) => mem_opts,
3731        };
3732
3733        // If the alignment is 1 then everything is trivially aligned and the
3734        // check can be omitted.
3735        if align != 1 {
3736            self.instruction(LocalGet(addr.idx));
3737            assert!(align.is_power_of_two());
3738            self.ptr_uconst(mem_opts, align - 1);
3739            self.ptr_and(mem_opts);
3740            self.ptr_if(mem_opts, BlockType::Empty);
3741            self.trap(Trap::UnalignedPointer);
3742            self.instruction(End);
3743        }
3744
3745        let extend_to_64 = |me: &mut Self| {
3746            if !mem_opts.memory64() {
3747                me.instruction(I64ExtendI32U);
3748            }
3749        };
3750
3751        self.instruction(Block(BlockType::Empty));
3752        self.instruction(Block(BlockType::Empty));
3753        let (memory, ty) = mem_opts.memory.unwrap();
3754
3755        // Calculate the full byte size of memory with `memory.size`. Note that
3756        // arithmetic here is done always in 64-bits to accommodate 4G memories.
3757        // Additionally it's assumed that 64-bit memories never fill up
3758        // entirely.
3759        self.instruction(MemorySize(memory.as_u32()));
3760        extend_to_64(self);
3761        self.instruction(I64Const(ty.page_size_log2.into()));
3762        self.instruction(I64Shl);
3763
3764        // Calculate the end address of the string. This is done by adding the
3765        // base pointer to the byte length. For 32-bit memories there's no need
3766        // to check for overflow since everything is extended to 64-bit, but for
3767        // 64-bit memories overflow is checked.
3768        self.instruction(LocalGet(addr.idx));
3769        extend_to_64(self);
3770        self.alloc_size(mem_opts, size);
3771        extend_to_64(self);
3772        self.instruction(I64Add);
3773        if mem_opts.memory64() {
3774            let tmp = self.local_tee_new_tmp(ValType::I64);
3775            self.instruction(LocalGet(addr.idx));
3776            self.ptr_lt_u(mem_opts);
3777            self.instruction(BrIf(0));
3778            self.instruction(LocalGet(tmp.idx));
3779            self.free_temp_local(tmp);
3780        }
3781
3782        // If the byte size of memory is greater than the final address of the
3783        // string then the string is invalid. Note that if it's precisely equal
3784        // then that's ok.
3785        self.instruction(I64GeU);
3786        self.instruction(BrIf(1));
3787
3788        self.instruction(End);
3789        self.trap(oob_trap);
3790        self.instruction(End);
3791    }
3792
3793    /// Generates a new local in this function of the `ty` specified,
3794    /// initializing it with the top value on the current wasm stack.
3795    ///
3796    /// The returned `TempLocal` must be freed after it is finished with
3797    /// `free_temp_local`.
3798    fn local_tee_new_tmp(&mut self, ty: ValType) -> TempLocal {
3799        self.gen_temp_local(ty, LocalTee)
3800    }
3801
3802    /// Same as `local_tee_new_tmp` but initializes the local with `LocalSet`
3803    /// instead of `LocalTee`.
3804    fn local_set_new_tmp(&mut self, ty: ValType) -> TempLocal {
3805        self.gen_temp_local(ty, LocalSet)
3806    }
3807
3808    fn local_get_tmp(&mut self, local: &TempLocal) {
3809        self.instruction(LocalGet(local.idx));
3810    }
3811
3812    fn gen_temp_local(&mut self, ty: ValType, insn: fn(u32) -> Instruction<'static>) -> TempLocal {
3813        // First check to see if any locals are available in this function which
3814        // were previously generated but are no longer in use.
3815        if let Some(idx) = self.free_locals.get_mut(&ty).and_then(|v| v.pop()) {
3816            self.instruction(insn(idx));
3817            return TempLocal {
3818                ty,
3819                idx,
3820                needs_free: true,
3821            };
3822        }
3823
3824        // Failing that generate a fresh new local.
3825        let locals = &mut self.module.funcs[self.result].locals;
3826        match locals.last_mut() {
3827            Some((cnt, prev_ty)) if ty == *prev_ty => *cnt += 1,
3828            _ => locals.push((1, ty)),
3829        }
3830        self.nlocals += 1;
3831        let idx = self.nlocals - 1;
3832        self.instruction(insn(idx));
3833        TempLocal {
3834            ty,
3835            idx,
3836            needs_free: true,
3837        }
3838    }
3839
3840    /// Used to release a `TempLocal` from a particular lexical scope to allow
3841    /// its possible reuse in later scopes.
3842    fn free_temp_local(&mut self, mut local: TempLocal) {
3843        assert!(local.needs_free);
3844        self.free_locals
3845            .entry(local.ty)
3846            .or_insert(Vec::new())
3847            .push(local.idx);
3848        local.needs_free = false;
3849    }
3850
3851    fn instruction(&mut self, instr: Instruction) {
3852        instr.encode(&mut self.code);
3853    }
3854
3855    fn trap(&mut self, trap: Trap) {
3856        let trap_func = self.module.import_trap();
3857        self.instruction(I32Const(trap as i32));
3858        self.instruction(Call(trap_func.as_u32()));
3859        self.instruction(Unreachable);
3860    }
3861
3862    /// Flushes out the current `code` instructions into the destination
3863    /// function.
3864    ///
3865    /// This is a noop if no instructions have been encoded yet.
3866    fn flush_code(&mut self) {
3867        if self.code.is_empty() {
3868            return;
3869        }
3870        self.module.funcs[self.result]
3871            .body
3872            .push(Body::Raw(mem::take(&mut self.code)));
3873    }
3874
3875    fn finish(mut self) {
3876        // Append the final `end` instruction which all functions require, and
3877        // then empty out the temporary buffer in `Compiler`.
3878        self.instruction(End);
3879        self.flush_code();
3880
3881        // Flag the function as "done" which helps with an assert later on in
3882        // emission that everything was eventually finished.
3883        self.module.funcs[self.result].filled_in = true;
3884    }
3885
3886    /// Fetches the value contained with the local specified by `stack` and
3887    /// converts it to `dst_ty`.
3888    ///
3889    /// This is only intended for use in primitive operations where `stack` is
3890    /// guaranteed to have only one local. The type of the local on the stack is
3891    /// then converted to `dst_ty` appropriately. Note that the types may be
3892    /// different due to the "flattening" of variant types.
3893    fn stack_get(&mut self, stack: &Stack<'_>, dst_ty: ValType) {
3894        assert_eq!(stack.locals.len(), 1);
3895        let (idx, src_ty) = stack.locals[0];
3896        self.instruction(LocalGet(idx));
3897        match (src_ty, dst_ty) {
3898            (ValType::I32, ValType::I32)
3899            | (ValType::I64, ValType::I64)
3900            | (ValType::F32, ValType::F32)
3901            | (ValType::F64, ValType::F64) => {}
3902
3903            (ValType::I32, ValType::F32) => self.instruction(F32ReinterpretI32),
3904            (ValType::I64, ValType::I32) => {
3905                self.assert_i64_upper_bits_not_set(idx);
3906                self.instruction(I32WrapI64);
3907            }
3908            (ValType::I64, ValType::F64) => self.instruction(F64ReinterpretI64),
3909            (ValType::I64, ValType::F32) => {
3910                self.assert_i64_upper_bits_not_set(idx);
3911                self.instruction(I32WrapI64);
3912                self.instruction(F32ReinterpretI32);
3913            }
3914
3915            // should not be possible given the `join` function for variants
3916            (ValType::I32, ValType::I64)
3917            | (ValType::I32, ValType::F64)
3918            | (ValType::F32, ValType::I32)
3919            | (ValType::F32, ValType::I64)
3920            | (ValType::F32, ValType::F64)
3921            | (ValType::F64, ValType::I32)
3922            | (ValType::F64, ValType::I64)
3923            | (ValType::F64, ValType::F32)
3924
3925            // not used in the component model
3926            | (ValType::Ref(_), _)
3927            | (_, ValType::Ref(_))
3928            | (ValType::V128, _)
3929            | (_, ValType::V128) => {
3930                panic!("cannot get {dst_ty:?} from {src_ty:?} local");
3931            }
3932        }
3933    }
3934
3935    fn assert_i64_upper_bits_not_set(&mut self, local: u32) {
3936        if !self.module.tunables.debug_adapter_modules {
3937            return;
3938        }
3939        self.instruction(LocalGet(local));
3940        self.instruction(I64Const(32));
3941        self.instruction(I64ShrU);
3942        self.instruction(I32WrapI64);
3943        self.instruction(If(BlockType::Empty));
3944        self.trap(Trap::DebugAssertUpperBitsUnset);
3945        self.instruction(End);
3946    }
3947
3948    /// Converts the top value on the WebAssembly stack which has type
3949    /// `src_ty` to `dst_tys[0]`.
3950    ///
3951    /// This is only intended for conversion of primitives where the `dst_tys`
3952    /// list is known to be of length 1.
3953    fn stack_set(&mut self, dst_tys: &[ValType], src_ty: ValType) {
3954        assert_eq!(dst_tys.len(), 1);
3955        let dst_ty = dst_tys[0];
3956        match (src_ty, dst_ty) {
3957            (ValType::I32, ValType::I32)
3958            | (ValType::I64, ValType::I64)
3959            | (ValType::F32, ValType::F32)
3960            | (ValType::F64, ValType::F64) => {}
3961
3962            (ValType::F32, ValType::I32) => self.instruction(I32ReinterpretF32),
3963            (ValType::I32, ValType::I64) => self.instruction(I64ExtendI32U),
3964            (ValType::F64, ValType::I64) => self.instruction(I64ReinterpretF64),
3965            (ValType::F32, ValType::I64) => {
3966                self.instruction(I32ReinterpretF32);
3967                self.instruction(I64ExtendI32U);
3968            }
3969
3970            // should not be possible given the `join` function for variants
3971            (ValType::I64, ValType::I32)
3972            | (ValType::F64, ValType::I32)
3973            | (ValType::I32, ValType::F32)
3974            | (ValType::I64, ValType::F32)
3975            | (ValType::F64, ValType::F32)
3976            | (ValType::I32, ValType::F64)
3977            | (ValType::I64, ValType::F64)
3978            | (ValType::F32, ValType::F64)
3979
3980            // not used in the component model
3981            | (ValType::Ref(_), _)
3982            | (_, ValType::Ref(_))
3983            | (ValType::V128, _)
3984            | (_, ValType::V128) => {
3985                panic!("cannot get {dst_ty:?} from {src_ty:?} local");
3986            }
3987        }
3988    }
3989
3990    fn i32_load8u(&mut self, mem: &Memory) {
3991        self.instruction(LocalGet(mem.addr.idx));
3992        self.instruction(I32Load8U(mem.memarg(0)));
3993    }
3994
3995    fn i32_load8s(&mut self, mem: &Memory) {
3996        self.instruction(LocalGet(mem.addr.idx));
3997        self.instruction(I32Load8S(mem.memarg(0)));
3998    }
3999
4000    fn i32_load16u(&mut self, mem: &Memory) {
4001        self.instruction(LocalGet(mem.addr.idx));
4002        self.instruction(I32Load16U(mem.memarg(1)));
4003    }
4004
4005    fn i32_load16s(&mut self, mem: &Memory) {
4006        self.instruction(LocalGet(mem.addr.idx));
4007        self.instruction(I32Load16S(mem.memarg(1)));
4008    }
4009
4010    fn i32_load(&mut self, mem: &Memory) {
4011        self.instruction(LocalGet(mem.addr.idx));
4012        self.instruction(I32Load(mem.memarg(2)));
4013    }
4014
4015    fn i64_load(&mut self, mem: &Memory) {
4016        self.instruction(LocalGet(mem.addr.idx));
4017        self.instruction(I64Load(mem.memarg(3)));
4018    }
4019
4020    fn ptr_load(&mut self, mem: &Memory) {
4021        if mem.mem_opts().memory64() {
4022            self.i64_load(mem);
4023        } else {
4024            self.i32_load(mem);
4025        }
4026    }
4027
4028    fn ptr_add(&mut self, opts: &LinearMemoryOptions) {
4029        if opts.memory64() {
4030            self.instruction(I64Add);
4031        } else {
4032            self.instruction(I32Add);
4033        }
4034    }
4035
4036    fn ptr_sub(&mut self, opts: &LinearMemoryOptions) {
4037        if opts.memory64() {
4038            self.instruction(I64Sub);
4039        } else {
4040            self.instruction(I32Sub);
4041        }
4042    }
4043
4044    fn ptr_mul(&mut self, opts: &LinearMemoryOptions) {
4045        if opts.memory64() {
4046            self.instruction(I64Mul);
4047        } else {
4048            self.instruction(I32Mul);
4049        }
4050    }
4051
4052    fn ptr_gt_u(&mut self, opts: &LinearMemoryOptions) {
4053        if opts.memory64() {
4054            self.instruction(I64GtU);
4055        } else {
4056            self.instruction(I32GtU);
4057        }
4058    }
4059
4060    fn ptr_lt_u(&mut self, opts: &LinearMemoryOptions) {
4061        if opts.memory64() {
4062            self.instruction(I64LtU);
4063        } else {
4064            self.instruction(I32LtU);
4065        }
4066    }
4067
4068    fn ptr_shl(&mut self, opts: &LinearMemoryOptions) {
4069        if opts.memory64() {
4070            self.instruction(I64Shl);
4071        } else {
4072            self.instruction(I32Shl);
4073        }
4074    }
4075
4076    fn ptr_eqz(&mut self, opts: &LinearMemoryOptions) {
4077        if opts.memory64() {
4078            self.instruction(I64Eqz);
4079        } else {
4080            self.instruction(I32Eqz);
4081        }
4082    }
4083
4084    fn ptr_uconst(&mut self, opts: &LinearMemoryOptions, val: u32) {
4085        if opts.memory64() {
4086            self.instruction(I64Const(val.into()));
4087        } else {
4088            self.instruction(I32Const(val.cast_signed()));
4089        }
4090    }
4091
4092    fn ptr_iconst(&mut self, opts: &LinearMemoryOptions, val: i32) {
4093        if opts.memory64() {
4094            self.instruction(I64Const(val.into()));
4095        } else {
4096            self.instruction(I32Const(val));
4097        }
4098    }
4099
4100    fn ptr_eq(&mut self, opts: &LinearMemoryOptions) {
4101        if opts.memory64() {
4102            self.instruction(I64Eq);
4103        } else {
4104            self.instruction(I32Eq);
4105        }
4106    }
4107
4108    fn ptr_ne(&mut self, opts: &LinearMemoryOptions) {
4109        if opts.memory64() {
4110            self.instruction(I64Ne);
4111        } else {
4112            self.instruction(I32Ne);
4113        }
4114    }
4115
4116    fn ptr_and(&mut self, opts: &LinearMemoryOptions) {
4117        if opts.memory64() {
4118            self.instruction(I64And);
4119        } else {
4120            self.instruction(I32And);
4121        }
4122    }
4123
4124    fn ptr_or(&mut self, opts: &LinearMemoryOptions) {
4125        if opts.memory64() {
4126            self.instruction(I64Or);
4127        } else {
4128            self.instruction(I32Or);
4129        }
4130    }
4131
4132    fn ptr_xor(&mut self, opts: &LinearMemoryOptions) {
4133        if opts.memory64() {
4134            self.instruction(I64Xor);
4135        } else {
4136            self.instruction(I32Xor);
4137        }
4138    }
4139
4140    fn ptr_if(&mut self, opts: &LinearMemoryOptions, ty: BlockType) {
4141        if opts.memory64() {
4142            self.instruction(I64Const(0));
4143            self.instruction(I64Ne);
4144        }
4145        self.instruction(If(ty));
4146    }
4147
4148    fn ptr_br_if(&mut self, opts: &LinearMemoryOptions, depth: u32) {
4149        if opts.memory64() {
4150            self.instruction(I64Const(0));
4151            self.instruction(I64Ne);
4152        }
4153        self.instruction(BrIf(depth));
4154    }
4155
4156    fn f32_load(&mut self, mem: &Memory) {
4157        self.instruction(LocalGet(mem.addr.idx));
4158        self.instruction(F32Load(mem.memarg(2)));
4159    }
4160
4161    fn f64_load(&mut self, mem: &Memory) {
4162        self.instruction(LocalGet(mem.addr.idx));
4163        self.instruction(F64Load(mem.memarg(3)));
4164    }
4165
4166    fn push_dst_addr(&mut self, dst: &Destination) {
4167        if let Destination::Memory(mem) = dst {
4168            self.instruction(LocalGet(mem.addr.idx));
4169        }
4170    }
4171
4172    fn i32_store8(&mut self, mem: &Memory) {
4173        self.instruction(I32Store8(mem.memarg(0)));
4174    }
4175
4176    fn i32_store16(&mut self, mem: &Memory) {
4177        self.instruction(I32Store16(mem.memarg(1)));
4178    }
4179
4180    fn i32_store(&mut self, mem: &Memory) {
4181        self.instruction(I32Store(mem.memarg(2)));
4182    }
4183
4184    fn i64_store(&mut self, mem: &Memory) {
4185        self.instruction(I64Store(mem.memarg(3)));
4186    }
4187
4188    fn ptr_store(&mut self, mem: &Memory) {
4189        if mem.mem_opts().memory64() {
4190            self.i64_store(mem);
4191        } else {
4192            self.i32_store(mem);
4193        }
4194    }
4195
4196    fn f32_store(&mut self, mem: &Memory) {
4197        self.instruction(F32Store(mem.memarg(2)));
4198    }
4199
4200    fn f64_store(&mut self, mem: &Memory) {
4201        self.instruction(F64Store(mem.memarg(3)));
4202    }
4203
4204    /// Push a pointer-typed value for `opts` on the wasm stack representing
4205    /// the `size` passed in.
4206    fn alloc_size(&mut self, opts: &LinearMemoryOptions, size: &AllocSize) {
4207        match size {
4208            AllocSize::Const(size) => self.ptr_uconst(opts, *size),
4209            AllocSize::Local(idx) => self.instruction(LocalGet(*idx)),
4210            AllocSize::DoubleLocal(idx) => {
4211                self.instruction(LocalGet(*idx));
4212                self.ptr_uconst(opts, 1);
4213                self.ptr_shl(opts);
4214            }
4215        }
4216    }
4217}
4218
4219impl<'a> Source<'a> {
4220    /// Given this `Source` returns an iterator over the `Source` for each of
4221    /// the component `fields` specified.
4222    ///
4223    /// This will automatically slice stack-based locals to the appropriate
4224    /// width for each component type and additionally calculate the appropriate
4225    /// offset for each memory-based type.
4226    fn record_field_srcs<'b>(
4227        &'b self,
4228        types: &'b ComponentTypesBuilder,
4229        fields: impl IntoIterator<Item = InterfaceType> + 'b,
4230    ) -> impl Iterator<Item = Source<'a>> + 'b
4231    where
4232        'a: 'b,
4233    {
4234        let mut offset = 0;
4235        fields.into_iter().map(move |ty| match self {
4236            Source::Memory(mem) => {
4237                let mem = next_field_offset(&mut offset, types, &ty, mem);
4238                Source::Memory(mem)
4239            }
4240            Source::Stack(stack) => {
4241                let cnt = types.flat_types(&ty).unwrap().len() as u32;
4242                offset += cnt;
4243                Source::Stack(stack.slice((offset - cnt) as usize..offset as usize))
4244            }
4245            Source::Struct(_) => todo!(),
4246            Source::Array(_) => todo!(),
4247        })
4248    }
4249
4250    /// Returns the corresponding discriminant source and payload source f
4251    fn payload_src(
4252        &self,
4253        types: &ComponentTypesBuilder,
4254        info: &VariantInfo,
4255        case: Option<&InterfaceType>,
4256    ) -> Source<'a> {
4257        match self {
4258            Source::Stack(s) => {
4259                let flat_len = match case {
4260                    Some(case) => types.flat_types(case).unwrap().len(),
4261                    None => 0,
4262                };
4263                Source::Stack(s.slice(1..s.locals.len()).slice(0..flat_len))
4264            }
4265            Source::Memory(mem) => {
4266                let mem = if mem.mem_opts().memory64() {
4267                    mem.bump(info.payload_offset64)
4268                } else {
4269                    mem.bump(info.payload_offset32)
4270                };
4271                Source::Memory(mem)
4272            }
4273            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
4274        }
4275    }
4276
4277    fn opts(&self) -> &'a Options {
4278        match self {
4279            Source::Stack(s) => s.opts,
4280            Source::Memory(mem) => mem.opts,
4281            Source::Struct(s) => s.opts,
4282            Source::Array(a) => a.opts,
4283        }
4284    }
4285}
4286
4287impl<'a> Destination<'a> {
4288    /// Same as `Source::record_field_srcs` but for destinations.
4289    fn record_field_dsts<'b, I>(
4290        &'b self,
4291        types: &'b ComponentTypesBuilder,
4292        fields: I,
4293    ) -> impl Iterator<Item = Destination<'b>> + use<'b, I>
4294    where
4295        'a: 'b,
4296        I: IntoIterator<Item = InterfaceType> + 'b,
4297    {
4298        let mut offset = 0;
4299        fields.into_iter().map(move |ty| match self {
4300            Destination::Memory(mem) => {
4301                let mem = next_field_offset(&mut offset, types, &ty, mem);
4302                Destination::Memory(mem)
4303            }
4304            Destination::Stack(s, opts) => {
4305                let cnt = types.flat_types(&ty).unwrap().len() as u32;
4306                offset += cnt;
4307                Destination::Stack(&s[(offset - cnt) as usize..offset as usize], opts)
4308            }
4309            Destination::Struct(_) => todo!(),
4310            Destination::Array(_) => todo!(),
4311        })
4312    }
4313
4314    /// Returns the corresponding discriminant source and payload source f
4315    fn payload_dst(
4316        &self,
4317        types: &ComponentTypesBuilder,
4318        info: &VariantInfo,
4319        case: Option<&InterfaceType>,
4320    ) -> Destination<'_> {
4321        match self {
4322            Destination::Stack(s, opts) => {
4323                let flat_len = match case {
4324                    Some(case) => types.flat_types(case).unwrap().len(),
4325                    None => 0,
4326                };
4327                Destination::Stack(&s[1..][..flat_len], opts)
4328            }
4329            Destination::Memory(mem) => {
4330                let mem = if mem.mem_opts().memory64() {
4331                    mem.bump(info.payload_offset64)
4332                } else {
4333                    mem.bump(info.payload_offset32)
4334                };
4335                Destination::Memory(mem)
4336            }
4337            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
4338        }
4339    }
4340
4341    fn opts(&self) -> &'a Options {
4342        match self {
4343            Destination::Stack(_, opts) => opts,
4344            Destination::Memory(mem) => mem.opts,
4345            Destination::Struct(s) => s.opts,
4346            Destination::Array(a) => a.opts,
4347        }
4348    }
4349}
4350
4351fn next_field_offset<'a>(
4352    offset: &mut u32,
4353    types: &ComponentTypesBuilder,
4354    field: &InterfaceType,
4355    mem: &Memory<'a>,
4356) -> Memory<'a> {
4357    let abi = types.canonical_abi(field);
4358    let offset = if mem.mem_opts().memory64() {
4359        abi.next_field64(offset)
4360    } else {
4361        abi.next_field32(offset)
4362    };
4363    mem.bump(offset)
4364}
4365
4366impl<'a> Memory<'a> {
4367    fn memarg(&self, align: u32) -> MemArg {
4368        MemArg {
4369            offset: u64::from(self.offset),
4370            align,
4371            memory_index: self.mem_opts().memory.unwrap().0.as_u32(),
4372        }
4373    }
4374
4375    fn bump(&self, offset: u32) -> Memory<'a> {
4376        Memory {
4377            opts: self.opts,
4378            addr: TempLocal::new(self.addr.idx, self.addr.ty),
4379            offset: self.offset + offset,
4380        }
4381    }
4382}
4383
4384impl<'a> Stack<'a> {
4385    fn slice(&self, range: Range<usize>) -> Stack<'a> {
4386        Stack {
4387            locals: &self.locals[range],
4388            opts: self.opts,
4389        }
4390    }
4391}
4392
4393struct VariantCase<'a> {
4394    src_i: u32,
4395    src_ty: Option<&'a InterfaceType>,
4396    dst_i: u32,
4397    dst_ty: Option<&'a InterfaceType>,
4398}
4399
4400fn variant_info<'a, I>(types: &ComponentTypesBuilder, cases: I) -> VariantInfo
4401where
4402    I: IntoIterator<Item = Option<&'a InterfaceType>>,
4403    I::IntoIter: ExactSizeIterator,
4404{
4405    VariantInfo::new(
4406        cases
4407            .into_iter()
4408            .map(|ty| ty.map(|ty| types.canonical_abi(ty))),
4409    )
4410    .0
4411}
4412
4413/// State for the iteration loop inside a sequence translation.
4414struct SequenceLoopState {
4415    remaining: TempLocal,
4416    cur_src_ptr: TempLocal,
4417    cur_dst_ptr: TempLocal,
4418}
4419
4420/// Holds all temporaries created by `begin_translate_sequence` so the
4421/// caller can emit a custom loop body before calling
4422/// `end_translate_sequence`.
4423struct SequenceTranslation<'a> {
4424    src_len: TempLocal,
4425    src_mem: Memory<'a>,
4426    dst_mem: Memory<'a>,
4427    src_opts: &'a Options,
4428    dst_opts: &'a Options,
4429    src_mem_opts: &'a LinearMemoryOptions,
4430    dst_mem_opts: &'a LinearMemoryOptions,
4431    loop_state: Option<SequenceLoopState>,
4432}
4433
4434enum AllocSize {
4435    Const(u32),
4436    Local(u32),
4437    DoubleLocal(u32),
4438}
4439
4440struct WasmString<'a> {
4441    ptr: TempLocal,
4442    len: TempLocal,
4443    opts: &'a Options,
4444}
4445
4446struct TempLocal {
4447    idx: u32,
4448    ty: ValType,
4449    needs_free: bool,
4450}
4451
4452impl TempLocal {
4453    fn new(idx: u32, ty: ValType) -> TempLocal {
4454        TempLocal {
4455            idx,
4456            ty,
4457            needs_free: false,
4458        }
4459    }
4460}
4461
4462impl std::ops::Drop for TempLocal {
4463    fn drop(&mut self) {
4464        if self.needs_free {
4465            panic!("temporary local not free'd");
4466        }
4467    }
4468}
4469
4470impl From<FlatType> for ValType {
4471    fn from(ty: FlatType) -> ValType {
4472        match ty {
4473            FlatType::I32 => ValType::I32,
4474            FlatType::I64 => ValType::I64,
4475            FlatType::F32 => ValType::F32,
4476            FlatType::F64 => ValType::F64,
4477        }
4478    }
4479}