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, 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;
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,
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 align = src_tys
947                .iter()
948                .map(|t| self.types.align(lower_mem_opts, t))
949                .max()
950                .unwrap_or(1);
951            Source::Memory(self.memory_operand(lower_opts, TempLocal::new(addr, ty), align))
952        };
953
954        let dst = if let Some(flat) = &dst_flat {
955            Destination::Stack(flat, lift_opts)
956        } else {
957            let abi = CanonicalAbiInfo::record(dst_tys.iter().map(|t| self.types.canonical_abi(t)));
958            match lift_opts.data_model {
959                DataModel::Gc {} => todo!("CM+GC"),
960                DataModel::LinearMemory(LinearMemoryOptions { memory64, .. }) => {
961                    let (size, align) = if memory64 {
962                        (abi.size64, abi.align64)
963                    } else {
964                        (abi.size32, abi.align32)
965                    };
966
967                    // If there are too many parameters then space is allocated in the
968                    // destination module for the parameters via its `realloc` function.
969                    let size = MallocSize::Const(size);
970                    Destination::Memory(self.malloc(lift_opts, size, align))
971                }
972            }
973        };
974
975        let srcs = src
976            .record_field_srcs(self.types, src_tys.iter().copied())
977            .zip(src_tys.iter());
978        let dsts = dst
979            .record_field_dsts(self.types, dst_tys.iter().copied())
980            .zip(dst_tys.iter());
981        for ((src, src_ty), (dst, dst_ty)) in srcs.zip(dsts) {
982            self.translate(&src_ty, &src, &dst_ty, &dst);
983        }
984
985        // If the destination was linear memory instead of the stack then the
986        // actual parameter that we're passing is the address of the values
987        // stored, so ensure that's happening in the wasm body here.
988        if let Destination::Memory(mem) = dst {
989            self.instruction(LocalGet(mem.addr.idx));
990            self.free_temp_local(mem.addr);
991        }
992    }
993
994    fn translate_results(
995        &mut self,
996        adapter: &AdapterData,
997        param_locals: &[(u32, ValType)],
998        result_locals: &[(u32, ValType)],
999    ) {
1000        let src_tys = self.types[adapter.lift.ty].results;
1001        let src_tys = self.types[src_tys]
1002            .types
1003            .iter()
1004            .copied()
1005            .collect::<Vec<_>>();
1006        let dst_tys = self.types[adapter.lower.ty].results;
1007        let dst_tys = self.types[dst_tys]
1008            .types
1009            .iter()
1010            .copied()
1011            .collect::<Vec<_>>();
1012        let lift_opts = &adapter.lift.options;
1013        let lower_opts = &adapter.lower.options;
1014
1015        let src_flat = self
1016            .types
1017            .flatten_lifting_types(lift_opts, src_tys.iter().copied());
1018        let dst_flat = self
1019            .types
1020            .flatten_lowering_types(lower_opts, dst_tys.iter().copied());
1021
1022        let src = if src_flat.is_some() {
1023            Source::Stack(Stack {
1024                locals: result_locals,
1025                opts: lift_opts,
1026            })
1027        } else {
1028            // The original results to read from in this case come from the
1029            // return value of the function itself. The imported function will
1030            // return a linear memory address at which the values can be read
1031            // from.
1032            let lift_mem_opts = lift_opts.data_model.unwrap_memory();
1033            let align = src_tys
1034                .iter()
1035                .map(|t| self.types.align(lift_mem_opts, t))
1036                .max()
1037                .unwrap_or(1);
1038            assert_eq!(
1039                result_locals.len(),
1040                if lower_opts.async_ || lift_opts.async_ {
1041                    2
1042                } else {
1043                    1
1044                }
1045            );
1046            let (addr, ty) = result_locals[0];
1047            assert_eq!(ty, lift_opts.data_model.unwrap_memory().ptr());
1048            Source::Memory(self.memory_operand(lift_opts, TempLocal::new(addr, ty), align))
1049        };
1050
1051        let dst = if let Some(flat) = &dst_flat {
1052            Destination::Stack(flat, lower_opts)
1053        } else {
1054            // This is slightly different than `translate_params` where the
1055            // return pointer was provided by the caller of this function
1056            // meaning the last parameter local is a pointer into linear memory.
1057            let lower_mem_opts = lower_opts.data_model.unwrap_memory();
1058            let align = dst_tys
1059                .iter()
1060                .map(|t| self.types.align(lower_mem_opts, t))
1061                .max()
1062                .unwrap_or(1);
1063            let (addr, ty) = *param_locals.last().expect("no retptr");
1064            assert_eq!(ty, lower_opts.data_model.unwrap_memory().ptr());
1065            Destination::Memory(self.memory_operand(lower_opts, TempLocal::new(addr, ty), align))
1066        };
1067
1068        let srcs = src
1069            .record_field_srcs(self.types, src_tys.iter().copied())
1070            .zip(src_tys.iter());
1071        let dsts = dst
1072            .record_field_dsts(self.types, dst_tys.iter().copied())
1073            .zip(dst_tys.iter());
1074        for ((src, src_ty), (dst, dst_ty)) in srcs.zip(dsts) {
1075            self.translate(&src_ty, &src, &dst_ty, &dst);
1076        }
1077    }
1078
1079    fn translate(
1080        &mut self,
1081        src_ty: &InterfaceType,
1082        src: &Source<'_>,
1083        dst_ty: &InterfaceType,
1084        dst: &Destination,
1085    ) {
1086        if let Source::Memory(mem) = src {
1087            self.assert_aligned(src_ty, mem);
1088        }
1089        if let Destination::Memory(mem) = dst {
1090            self.assert_aligned(dst_ty, mem);
1091        }
1092
1093        // Calculate a cost heuristic for what the translation of this specific
1094        // layer of the type is going to incur. The purpose of this cost is that
1095        // we'll deduct it from `self.fuel` and if no fuel is remaining then
1096        // translation is outlined into a separate function rather than being
1097        // translated into this function.
1098        //
1099        // The general goal is to avoid creating an exponentially sized function
1100        // for a linearly sized input (the type section). By outlining helper
1101        // functions there will ideally be a constant set of helper functions
1102        // per type (to accommodate in-memory or on-stack transfers as well as
1103        // src/dst options) which means that each function is at most a certain
1104        // size and we have a linear number of functions which should guarantee
1105        // an overall linear size of the output.
1106        //
1107        // To implement this the current heuristic is that each layer of
1108        // translating a type has a cost associated with it and this cost is
1109        // accounted for in `self.fuel`. Some conversions are considered free as
1110        // they generate basically as much code as the `call` to the translation
1111        // function while other are considered proportionally expensive to the
1112        // size of the type. The hope is that some upper layers are of a type's
1113        // translation are all inlined into one function but bottom layers end
1114        // up getting outlined to separate functions. Theoretically, again this
1115        // is built on hopes and dreams, the outlining can be shared amongst
1116        // tightly-intertwined type hierarchies which will reduce the size of
1117        // the output module due to the helpers being used.
1118        //
1119        // This heuristic of how to split functions has changed a few times in
1120        // the past and this isn't necessarily guaranteed to be the final
1121        // iteration.
1122        let cost = match src_ty {
1123            // These types are all quite simple to load/store and equate to
1124            // basically the same cost of the `call` instruction to call an
1125            // out-of-line translation function, so give them 0 cost.
1126            InterfaceType::Bool
1127            | InterfaceType::U8
1128            | InterfaceType::S8
1129            | InterfaceType::U16
1130            | InterfaceType::S16
1131            | InterfaceType::U32
1132            | InterfaceType::S32
1133            | InterfaceType::U64
1134            | InterfaceType::S64
1135            | InterfaceType::Float32
1136            | InterfaceType::Float64 => 0,
1137
1138            // This has a small amount of validation associated with it, so
1139            // give it a cost of 1.
1140            InterfaceType::Char => 1,
1141
1142            // This has a fair bit of code behind it depending on the
1143            // strings/encodings in play, so arbitrarily assign it this cost.
1144            InterfaceType::String => 40,
1145
1146            // Iteration of a loop is along the lines of the cost of a string
1147            // so give it the same cost
1148            InterfaceType::List(_) => 40,
1149            // Maps are similar to lists in terms of iteration cost
1150            InterfaceType::Map(_) => 40,
1151
1152            InterfaceType::Flags(i) => {
1153                let count = self.module.types[*i].names.len();
1154                match FlagsSize::from_count(count) {
1155                    FlagsSize::Size0 => 0,
1156                    FlagsSize::Size1 | FlagsSize::Size2 => 1,
1157                    FlagsSize::Size4Plus(n) => n.into(),
1158                }
1159            }
1160
1161            InterfaceType::Record(i) => self.types[*i].fields.len(),
1162            InterfaceType::Tuple(i) => self.types[*i].types.len(),
1163            InterfaceType::Variant(i) => self.types[*i].cases.len(),
1164            InterfaceType::Enum(i) => self.types[*i].names.len(),
1165
1166            // 2 cases to consider for each of these variants.
1167            InterfaceType::Option(_) | InterfaceType::Result(_) => 2,
1168
1169            // TODO(#6696) - something nonzero, is 1 right?
1170            InterfaceType::Own(_)
1171            | InterfaceType::Borrow(_)
1172            | InterfaceType::Future(_)
1173            | InterfaceType::Stream(_)
1174            | InterfaceType::ErrorContext(_) => 1,
1175            InterfaceType::FixedLengthList(i) => self.types[*i].size as usize,
1176        };
1177
1178        match self.fuel.checked_sub(cost) {
1179            // This function has enough fuel to perform the layer of translation
1180            // necessary for this type, so the fuel is updated in-place and
1181            // translation continues. Note that the recursion here is bounded by
1182            // the static recursion limit for all interface types as imposed
1183            // during the translation phase.
1184            Some(n) => {
1185                self.fuel = n;
1186                match src_ty {
1187                    InterfaceType::Bool => self.translate_bool(src, dst_ty, dst),
1188                    InterfaceType::U8 => self.translate_u8(src, dst_ty, dst),
1189                    InterfaceType::S8 => self.translate_s8(src, dst_ty, dst),
1190                    InterfaceType::U16 => self.translate_u16(src, dst_ty, dst),
1191                    InterfaceType::S16 => self.translate_s16(src, dst_ty, dst),
1192                    InterfaceType::U32 => self.translate_u32(src, dst_ty, dst),
1193                    InterfaceType::S32 => self.translate_s32(src, dst_ty, dst),
1194                    InterfaceType::U64 => self.translate_u64(src, dst_ty, dst),
1195                    InterfaceType::S64 => self.translate_s64(src, dst_ty, dst),
1196                    InterfaceType::Float32 => self.translate_f32(src, dst_ty, dst),
1197                    InterfaceType::Float64 => self.translate_f64(src, dst_ty, dst),
1198                    InterfaceType::Char => self.translate_char(src, dst_ty, dst),
1199                    InterfaceType::String => self.translate_string(src, dst_ty, dst),
1200                    InterfaceType::List(t) => self.translate_list(*t, src, dst_ty, dst),
1201                    InterfaceType::Map(t) => self.translate_map(*t, src, dst_ty, dst),
1202                    InterfaceType::Record(t) => self.translate_record(*t, src, dst_ty, dst),
1203                    InterfaceType::Flags(f) => self.translate_flags(*f, src, dst_ty, dst),
1204                    InterfaceType::Tuple(t) => self.translate_tuple(*t, src, dst_ty, dst),
1205                    InterfaceType::Variant(v) => self.translate_variant(*v, src, dst_ty, dst),
1206                    InterfaceType::Enum(t) => self.translate_enum(*t, src, dst_ty, dst),
1207                    InterfaceType::Option(t) => self.translate_option(*t, src, dst_ty, dst),
1208                    InterfaceType::Result(t) => self.translate_result(*t, src, dst_ty, dst),
1209                    InterfaceType::Own(t) => self.translate_own(*t, src, dst_ty, dst),
1210                    InterfaceType::Borrow(t) => self.translate_borrow(*t, src, dst_ty, dst),
1211                    InterfaceType::Future(t) => self.translate_future(*t, src, dst_ty, dst),
1212                    InterfaceType::Stream(t) => self.translate_stream(*t, src, dst_ty, dst),
1213                    InterfaceType::ErrorContext(t) => {
1214                        self.translate_error_context(*t, src, dst_ty, dst)
1215                    }
1216                    InterfaceType::FixedLengthList(t) => {
1217                        self.translate_fixed_length_list(*t, src, dst_ty, dst);
1218                    }
1219                }
1220            }
1221
1222            // This function does not have enough fuel left to perform this
1223            // layer of translation so the translation is deferred to a helper
1224            // function. The actual translation here is then done by marshalling
1225            // the src/dst into the function we're calling and then processing
1226            // the results.
1227            None => {
1228                let src_loc = match src {
1229                    // If the source is on the stack then `stack_get` is used to
1230                    // convert everything to the appropriate flat representation
1231                    // for the source type.
1232                    Source::Stack(stack) => {
1233                        for (i, ty) in stack
1234                            .opts
1235                            .flat_types(src_ty, self.types)
1236                            .unwrap()
1237                            .iter()
1238                            .enumerate()
1239                        {
1240                            let stack = stack.slice(i..i + 1);
1241                            self.stack_get(&stack, (*ty).into());
1242                        }
1243                        HelperLocation::Stack
1244                    }
1245                    // If the source is in memory then the pointer is passed
1246                    // through, but note that the offset must be factored in
1247                    // here since the translation function will start from
1248                    // offset 0.
1249                    Source::Memory(mem) => {
1250                        self.push_mem_addr(mem);
1251                        HelperLocation::Memory
1252                    }
1253                    Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1254                };
1255                let dst_loc = match dst {
1256                    Destination::Stack(..) => HelperLocation::Stack,
1257                    Destination::Memory(mem) => {
1258                        self.push_mem_addr(mem);
1259                        HelperLocation::Memory
1260                    }
1261                    Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1262                };
1263                // Generate a `FunctionId` corresponding to the `Helper`
1264                // configuration that is necessary here. This will ideally be a
1265                // "cache hit" and use a preexisting helper which represents
1266                // outlining what would otherwise be duplicate code within a
1267                // function to one function.
1268                let helper = self.module.translate_helper(Helper {
1269                    src: HelperType {
1270                        ty: *src_ty,
1271                        opts: *src.opts(),
1272                        loc: src_loc,
1273                    },
1274                    dst: HelperType {
1275                        ty: *dst_ty,
1276                        opts: *dst.opts(),
1277                        loc: dst_loc,
1278                    },
1279                });
1280                // Emit a `call` instruction which will get "relocated" to a
1281                // function index once translation has completely finished.
1282                self.flush_code();
1283                self.module.funcs[self.result].body.push(Body::Call(helper));
1284
1285                // If the destination of the translation was on the stack then
1286                // the types on the stack need to be optionally converted to
1287                // different types (e.g. if the result here is part of a variant
1288                // somewhere else).
1289                //
1290                // This translation happens inline here by popping the results
1291                // into new locals and then using those locals to do a
1292                // `stack_set`.
1293                if let Destination::Stack(tys, opts) = dst {
1294                    let flat = self
1295                        .types
1296                        .flatten_types(opts, usize::MAX, [*dst_ty])
1297                        .unwrap();
1298                    assert_eq!(flat.len(), tys.len());
1299                    let locals = flat
1300                        .iter()
1301                        .rev()
1302                        .map(|ty| self.local_set_new_tmp(*ty))
1303                        .collect::<Vec<_>>();
1304                    for (ty, local) in tys.iter().zip(locals.into_iter().rev()) {
1305                        self.instruction(LocalGet(local.idx));
1306                        self.stack_set(std::slice::from_ref(ty), local.ty);
1307                        self.free_temp_local(local);
1308                    }
1309                }
1310            }
1311        }
1312    }
1313
1314    fn push_mem_addr(&mut self, mem: &Memory<'_>) {
1315        self.instruction(LocalGet(mem.addr.idx));
1316        if mem.offset != 0 {
1317            self.ptr_uconst(mem.mem_opts(), mem.offset);
1318            self.ptr_add(mem.mem_opts());
1319        }
1320    }
1321
1322    fn translate_bool(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1323        // TODO: subtyping
1324        assert!(matches!(dst_ty, InterfaceType::Bool));
1325        self.push_dst_addr(dst);
1326
1327        // Booleans are canonicalized to 0 or 1 as they pass through the
1328        // component boundary, so use a `select` instruction to do so.
1329        self.instruction(I32Const(1));
1330        self.instruction(I32Const(0));
1331        match src {
1332            Source::Memory(mem) => self.i32_load8u(mem),
1333            Source::Stack(stack) => self.stack_get(stack, ValType::I32),
1334            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1335        }
1336        self.instruction(Select);
1337
1338        match dst {
1339            Destination::Memory(mem) => self.i32_store8(mem),
1340            Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1341            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1342        }
1343    }
1344
1345    fn translate_u8(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1346        // TODO: subtyping
1347        assert!(matches!(dst_ty, InterfaceType::U8));
1348        self.convert_u8_mask(src, dst, 0xff);
1349    }
1350
1351    fn convert_u8_mask(&mut self, src: &Source<'_>, dst: &Destination<'_>, mask: u8) {
1352        self.push_dst_addr(dst);
1353        let mut needs_mask = true;
1354        match src {
1355            Source::Memory(mem) => {
1356                self.i32_load8u(mem);
1357                needs_mask = mask != 0xff;
1358            }
1359            Source::Stack(stack) => {
1360                self.stack_get(stack, ValType::I32);
1361            }
1362            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1363        }
1364        if needs_mask {
1365            self.instruction(I32Const(i32::from(mask)));
1366            self.instruction(I32And);
1367        }
1368        match dst {
1369            Destination::Memory(mem) => self.i32_store8(mem),
1370            Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1371            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1372        }
1373    }
1374
1375    fn translate_s8(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1376        // TODO: subtyping
1377        assert!(matches!(dst_ty, InterfaceType::S8));
1378        self.push_dst_addr(dst);
1379        match src {
1380            Source::Memory(mem) => self.i32_load8s(mem),
1381            Source::Stack(stack) => {
1382                self.stack_get(stack, ValType::I32);
1383                self.instruction(I32Extend8S);
1384            }
1385            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1386        }
1387        match dst {
1388            Destination::Memory(mem) => self.i32_store8(mem),
1389            Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1390            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1391        }
1392    }
1393
1394    fn translate_u16(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1395        // TODO: subtyping
1396        assert!(matches!(dst_ty, InterfaceType::U16));
1397        self.convert_u16_mask(src, dst, 0xffff);
1398    }
1399
1400    fn convert_u16_mask(&mut self, src: &Source<'_>, dst: &Destination<'_>, mask: u16) {
1401        self.push_dst_addr(dst);
1402        let mut needs_mask = true;
1403        match src {
1404            Source::Memory(mem) => {
1405                self.i32_load16u(mem);
1406                needs_mask = mask != 0xffff;
1407            }
1408            Source::Stack(stack) => {
1409                self.stack_get(stack, ValType::I32);
1410            }
1411            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1412        }
1413        if needs_mask {
1414            self.instruction(I32Const(i32::from(mask)));
1415            self.instruction(I32And);
1416        }
1417        match dst {
1418            Destination::Memory(mem) => self.i32_store16(mem),
1419            Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1420            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1421        }
1422    }
1423
1424    fn translate_s16(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1425        // TODO: subtyping
1426        assert!(matches!(dst_ty, InterfaceType::S16));
1427        self.push_dst_addr(dst);
1428        match src {
1429            Source::Memory(mem) => self.i32_load16s(mem),
1430            Source::Stack(stack) => {
1431                self.stack_get(stack, ValType::I32);
1432                self.instruction(I32Extend16S);
1433            }
1434            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1435        }
1436        match dst {
1437            Destination::Memory(mem) => self.i32_store16(mem),
1438            Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1439            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1440        }
1441    }
1442
1443    fn translate_u32(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1444        // TODO: subtyping
1445        assert!(matches!(dst_ty, InterfaceType::U32));
1446        self.convert_u32_mask(src, dst, 0xffffffff)
1447    }
1448
1449    fn convert_u32_mask(&mut self, src: &Source<'_>, dst: &Destination<'_>, mask: u32) {
1450        self.push_dst_addr(dst);
1451        match src {
1452            Source::Memory(mem) => self.i32_load(mem),
1453            Source::Stack(stack) => self.stack_get(stack, ValType::I32),
1454            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1455        }
1456        if mask != 0xffffffff {
1457            self.instruction(I32Const(mask as i32));
1458            self.instruction(I32And);
1459        }
1460        match dst {
1461            Destination::Memory(mem) => self.i32_store(mem),
1462            Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1463            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1464        }
1465    }
1466
1467    fn translate_s32(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1468        // TODO: subtyping
1469        assert!(matches!(dst_ty, InterfaceType::S32));
1470        self.push_dst_addr(dst);
1471        match src {
1472            Source::Memory(mem) => self.i32_load(mem),
1473            Source::Stack(stack) => self.stack_get(stack, ValType::I32),
1474            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1475        }
1476        match dst {
1477            Destination::Memory(mem) => self.i32_store(mem),
1478            Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1479            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1480        }
1481    }
1482
1483    fn translate_u64(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1484        // TODO: subtyping
1485        assert!(matches!(dst_ty, InterfaceType::U64));
1486        self.push_dst_addr(dst);
1487        match src {
1488            Source::Memory(mem) => self.i64_load(mem),
1489            Source::Stack(stack) => self.stack_get(stack, ValType::I64),
1490            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1491        }
1492        match dst {
1493            Destination::Memory(mem) => self.i64_store(mem),
1494            Destination::Stack(stack, _) => self.stack_set(stack, ValType::I64),
1495            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1496        }
1497    }
1498
1499    fn translate_s64(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1500        // TODO: subtyping
1501        assert!(matches!(dst_ty, InterfaceType::S64));
1502        self.push_dst_addr(dst);
1503        match src {
1504            Source::Memory(mem) => self.i64_load(mem),
1505            Source::Stack(stack) => self.stack_get(stack, ValType::I64),
1506            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1507        }
1508        match dst {
1509            Destination::Memory(mem) => self.i64_store(mem),
1510            Destination::Stack(stack, _) => self.stack_set(stack, ValType::I64),
1511            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1512        }
1513    }
1514
1515    fn translate_f32(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1516        // TODO: subtyping
1517        assert!(matches!(dst_ty, InterfaceType::Float32));
1518        self.push_dst_addr(dst);
1519        match src {
1520            Source::Memory(mem) => self.f32_load(mem),
1521            Source::Stack(stack) => self.stack_get(stack, ValType::F32),
1522            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1523        }
1524        match dst {
1525            Destination::Memory(mem) => self.f32_store(mem),
1526            Destination::Stack(stack, _) => self.stack_set(stack, ValType::F32),
1527            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1528        }
1529    }
1530
1531    fn translate_f64(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1532        // TODO: subtyping
1533        assert!(matches!(dst_ty, InterfaceType::Float64));
1534        self.push_dst_addr(dst);
1535        match src {
1536            Source::Memory(mem) => self.f64_load(mem),
1537            Source::Stack(stack) => self.stack_get(stack, ValType::F64),
1538            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1539        }
1540        match dst {
1541            Destination::Memory(mem) => self.f64_store(mem),
1542            Destination::Stack(stack, _) => self.stack_set(stack, ValType::F64),
1543            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1544        }
1545    }
1546
1547    fn translate_char(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1548        assert!(matches!(dst_ty, InterfaceType::Char));
1549        match src {
1550            Source::Memory(mem) => self.i32_load(mem),
1551            Source::Stack(stack) => self.stack_get(stack, ValType::I32),
1552            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1553        }
1554        let local = self.local_set_new_tmp(ValType::I32);
1555
1556        // This sequence is copied from the output of LLVM for:
1557        //
1558        //      pub extern "C" fn foo(x: u32) -> char {
1559        //          char::try_from(x)
1560        //              .unwrap_or_else(|_| std::arch::wasm32::unreachable())
1561        //      }
1562        //
1563        // Apparently this does what's required by the canonical ABI:
1564        //
1565        //    def i32_to_char(opts, i):
1566        //      trap_if(i >= 0x110000)
1567        //      trap_if(0xD800 <= i <= 0xDFFF)
1568        //      return chr(i)
1569        //
1570        // ... but I don't know how it works other than "well I trust LLVM"
1571        self.instruction(Block(BlockType::Empty));
1572        self.instruction(Block(BlockType::Empty));
1573        self.instruction(LocalGet(local.idx));
1574        self.instruction(I32Const(0xd800));
1575        self.instruction(I32Xor);
1576        self.instruction(I32Const(-0x110000));
1577        self.instruction(I32Add);
1578        self.instruction(I32Const(-0x10f800));
1579        self.instruction(I32LtU);
1580        self.instruction(BrIf(0));
1581        self.instruction(LocalGet(local.idx));
1582        self.instruction(I32Const(0x110000));
1583        self.instruction(I32Ne);
1584        self.instruction(BrIf(1));
1585        self.instruction(End);
1586        self.trap(Trap::InvalidChar);
1587        self.instruction(End);
1588
1589        self.push_dst_addr(dst);
1590        self.instruction(LocalGet(local.idx));
1591        match dst {
1592            Destination::Memory(mem) => {
1593                self.i32_store(mem);
1594            }
1595            Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1596            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1597        }
1598
1599        self.free_temp_local(local);
1600    }
1601
1602    fn translate_string(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1603        assert!(matches!(dst_ty, InterfaceType::String));
1604        let src_opts = src.opts();
1605        let dst_opts = dst.opts();
1606
1607        let src_mem_opts = match &src_opts.data_model {
1608            DataModel::Gc {} => todo!("CM+GC"),
1609            DataModel::LinearMemory(opts) => opts,
1610        };
1611        let dst_mem_opts = match &dst_opts.data_model {
1612            DataModel::Gc {} => todo!("CM+GC"),
1613            DataModel::LinearMemory(opts) => opts,
1614        };
1615
1616        // Load the pointer/length of this string into temporary locals. These
1617        // will be referenced a good deal so this just makes it easier to deal
1618        // with them consistently below rather than trying to reload from memory
1619        // for example.
1620        match src {
1621            Source::Stack(s) => {
1622                assert_eq!(s.locals.len(), 2);
1623                self.stack_get(&s.slice(0..1), src_mem_opts.ptr());
1624                self.stack_get(&s.slice(1..2), src_mem_opts.ptr());
1625            }
1626            Source::Memory(mem) => {
1627                self.ptr_load(mem);
1628                self.ptr_load(&mem.bump(src_mem_opts.ptr_size().into()));
1629            }
1630            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1631        }
1632        let src_len = self.local_set_new_tmp(src_mem_opts.ptr());
1633        let src_ptr = self.local_set_new_tmp(src_mem_opts.ptr());
1634        let src_str = WasmString {
1635            ptr: src_ptr,
1636            len: src_len,
1637            opts: src_opts,
1638        };
1639
1640        let dst_str = match src_opts.string_encoding {
1641            StringEncoding::Utf8 => match dst_opts.string_encoding {
1642                StringEncoding::Utf8 => self.string_copy(&src_str, FE::Utf8, dst_opts, FE::Utf8),
1643                StringEncoding::Utf16 => self.string_utf8_to_utf16(&src_str, dst_opts),
1644                StringEncoding::CompactUtf16 => {
1645                    self.string_to_compact(&src_str, FE::Utf8, dst_opts)
1646                }
1647            },
1648
1649            StringEncoding::Utf16 => {
1650                self.verify_aligned(src_mem_opts, src_str.ptr.idx, 2);
1651                match dst_opts.string_encoding {
1652                    StringEncoding::Utf8 => {
1653                        self.string_deflate_to_utf8(&src_str, FE::Utf16, dst_opts)
1654                    }
1655                    StringEncoding::Utf16 => {
1656                        self.string_copy(&src_str, FE::Utf16, dst_opts, FE::Utf16)
1657                    }
1658                    StringEncoding::CompactUtf16 => {
1659                        self.string_to_compact(&src_str, FE::Utf16, dst_opts)
1660                    }
1661                }
1662            }
1663
1664            StringEncoding::CompactUtf16 => {
1665                self.verify_aligned(src_mem_opts, src_str.ptr.idx, 2);
1666
1667                // Test the tag big to see if this is a utf16 or a latin1 string
1668                // at runtime...
1669                self.instruction(LocalGet(src_str.len.idx));
1670                self.ptr_uconst(src_mem_opts, UTF16_TAG);
1671                self.ptr_and(src_mem_opts);
1672                self.ptr_if(src_mem_opts, BlockType::Empty);
1673
1674                // In the utf16 block unset the upper bit from the length local
1675                // so further calculations have the right value. Afterwards the
1676                // string transcode proceeds assuming utf16.
1677                self.instruction(LocalGet(src_str.len.idx));
1678                self.ptr_uconst(src_mem_opts, UTF16_TAG);
1679                self.ptr_xor(src_mem_opts);
1680                self.instruction(LocalSet(src_str.len.idx));
1681                let s1 = match dst_opts.string_encoding {
1682                    StringEncoding::Utf8 => {
1683                        self.string_deflate_to_utf8(&src_str, FE::Utf16, dst_opts)
1684                    }
1685                    StringEncoding::Utf16 => {
1686                        self.string_copy(&src_str, FE::Utf16, dst_opts, FE::Utf16)
1687                    }
1688                    StringEncoding::CompactUtf16 => {
1689                        self.string_compact_utf16_to_compact(&src_str, dst_opts)
1690                    }
1691                };
1692
1693                self.instruction(Else);
1694
1695                // In the latin1 block the `src_len` local is already the number
1696                // of code units, so the string transcoding is all that needs to
1697                // happen.
1698                let s2 = match dst_opts.string_encoding {
1699                    StringEncoding::Utf16 => {
1700                        self.string_copy(&src_str, FE::Latin1, dst_opts, FE::Utf16)
1701                    }
1702                    StringEncoding::Utf8 => {
1703                        self.string_deflate_to_utf8(&src_str, FE::Latin1, dst_opts)
1704                    }
1705                    StringEncoding::CompactUtf16 => {
1706                        self.string_copy(&src_str, FE::Latin1, dst_opts, FE::Latin1)
1707                    }
1708                };
1709                // Set our `s2` generated locals to the `s2` generated locals
1710                // as the resulting pointer of this transcode.
1711                self.instruction(LocalGet(s2.ptr.idx));
1712                self.instruction(LocalSet(s1.ptr.idx));
1713                self.instruction(LocalGet(s2.len.idx));
1714                self.instruction(LocalSet(s1.len.idx));
1715                self.instruction(End);
1716                self.free_temp_local(s2.ptr);
1717                self.free_temp_local(s2.len);
1718                s1
1719            }
1720        };
1721
1722        // Store the ptr/length in the desired destination
1723        match dst {
1724            Destination::Stack(s, _) => {
1725                self.instruction(LocalGet(dst_str.ptr.idx));
1726                self.stack_set(&s[..1], dst_mem_opts.ptr());
1727                self.instruction(LocalGet(dst_str.len.idx));
1728                self.stack_set(&s[1..], dst_mem_opts.ptr());
1729            }
1730            Destination::Memory(mem) => {
1731                self.instruction(LocalGet(mem.addr.idx));
1732                self.instruction(LocalGet(dst_str.ptr.idx));
1733                self.ptr_store(mem);
1734                self.instruction(LocalGet(mem.addr.idx));
1735                self.instruction(LocalGet(dst_str.len.idx));
1736                self.ptr_store(&mem.bump(dst_mem_opts.ptr_size().into()));
1737            }
1738            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1739        }
1740
1741        self.free_temp_local(src_str.ptr);
1742        self.free_temp_local(src_str.len);
1743        self.free_temp_local(dst_str.ptr);
1744        self.free_temp_local(dst_str.len);
1745    }
1746
1747    // Corresponding function for `store_string_copy` in the spec.
1748    //
1749    // This performs a transcoding of the string with a one-pass copy from
1750    // the `src` encoding to the `dst` encoding. This is only possible for
1751    // fixed encodings where the first allocation is guaranteed to be an
1752    // appropriate fit so it's not suitable for all encodings.
1753    //
1754    // Imported host transcoding functions here take the src/dst pointers as
1755    // well as the number of code units in the source (which always matches
1756    // the number of code units in the destination). There is no return
1757    // value from the transcode function since the encoding should always
1758    // work on the first pass.
1759    fn string_copy<'c>(
1760        &mut self,
1761        src: &WasmString<'_>,
1762        src_enc: FE,
1763        dst_opts: &'c Options,
1764        dst_enc: FE,
1765    ) -> WasmString<'c> {
1766        assert!(dst_enc.width() >= src_enc.width());
1767        self.validate_string_length(src, dst_enc);
1768
1769        let src_mem_opts = {
1770            match &src.opts.data_model {
1771                DataModel::Gc {} => todo!("CM+GC"),
1772                DataModel::LinearMemory(opts) => opts,
1773            }
1774        };
1775        let dst_mem_opts = {
1776            match &dst_opts.data_model {
1777                DataModel::Gc {} => todo!("CM+GC"),
1778                DataModel::LinearMemory(opts) => opts,
1779            }
1780        };
1781
1782        // Calculate the source byte length given the size of each code
1783        // unit. Note that this shouldn't overflow given
1784        // `validate_string_length` above.
1785        let mut src_byte_len_tmp = None;
1786        let src_byte_len = if src_enc.width() == 1 {
1787            src.len.idx
1788        } else {
1789            assert_eq!(src_enc.width(), 2);
1790            self.instruction(LocalGet(src.len.idx));
1791            self.ptr_uconst(src_mem_opts, 1);
1792            self.ptr_shl(src_mem_opts);
1793            let tmp = self.local_set_new_tmp(src.opts.data_model.unwrap_memory().ptr());
1794            let ret = tmp.idx;
1795            src_byte_len_tmp = Some(tmp);
1796            ret
1797        };
1798
1799        // Convert the source code units length to the destination byte
1800        // length type.
1801        self.convert_src_len_to_dst(
1802            src.len.idx,
1803            src.opts.data_model.unwrap_memory().ptr(),
1804            dst_opts.data_model.unwrap_memory().ptr(),
1805        );
1806        let dst_len = self.local_tee_new_tmp(dst_opts.data_model.unwrap_memory().ptr());
1807        if dst_enc.width() > 1 {
1808            assert_eq!(dst_enc.width(), 2);
1809            self.ptr_uconst(dst_mem_opts, 1);
1810            self.ptr_shl(dst_mem_opts);
1811        }
1812        let dst_byte_len = self.local_set_new_tmp(dst_opts.data_model.unwrap_memory().ptr());
1813
1814        // Allocate space in the destination using the calculated byte
1815        // length.
1816        let dst = {
1817            let dst_mem = self.malloc(
1818                dst_opts,
1819                MallocSize::Local(dst_byte_len.idx),
1820                dst_enc.width().into(),
1821            );
1822            WasmString {
1823                ptr: dst_mem.addr,
1824                len: dst_len,
1825                opts: dst_opts,
1826            }
1827        };
1828
1829        // Validate that `src_len + src_ptr` and
1830        // `dst_mem.addr_local + dst_byte_len` are both in-bounds. This
1831        // is done by loading the last byte of the string and if that
1832        // doesn't trap then it's known valid.
1833        self.validate_string_inbounds(src, src_byte_len);
1834        self.validate_string_inbounds(&dst, dst_byte_len.idx);
1835
1836        // If the validations pass then the host `transcode` intrinsic
1837        // is invoked. This will either raise a trap or otherwise succeed
1838        // in which case we're done.
1839        let op = if src_enc == dst_enc {
1840            Transcode::Copy(src_enc)
1841        } else {
1842            assert_eq!(src_enc, FE::Latin1);
1843            assert_eq!(dst_enc, FE::Utf16);
1844            Transcode::Latin1ToUtf16
1845        };
1846        let transcode = self.transcoder(src, &dst, op);
1847        self.instruction(LocalGet(src.ptr.idx));
1848        self.instruction(LocalGet(src.len.idx));
1849        self.instruction(LocalGet(dst.ptr.idx));
1850        self.instruction(Call(transcode.as_u32()));
1851
1852        self.free_temp_local(dst_byte_len);
1853        if let Some(tmp) = src_byte_len_tmp {
1854            self.free_temp_local(tmp);
1855        }
1856
1857        dst
1858    }
1859    // Corresponding function for `store_string_to_utf8` in the spec.
1860    //
1861    // This translation works by possibly performing a number of
1862    // reallocations. First a buffer of size input-code-units is used to try
1863    // to get the transcoding correct on the first try. If that fails the
1864    // maximum worst-case size is used and then that is resized down if it's
1865    // too large.
1866    //
1867    // The host transcoding function imported here will receive src ptr/len
1868    // and dst ptr/len and return how many code units were consumed on both
1869    // sides. The amount of code units consumed in the source dictates which
1870    // branches are taken in this conversion.
1871    fn string_deflate_to_utf8<'c>(
1872        &mut self,
1873        src: &WasmString<'_>,
1874        src_enc: FE,
1875        dst_opts: &'c Options,
1876    ) -> WasmString<'c> {
1877        let src_mem_opts = match &src.opts.data_model {
1878            DataModel::Gc {} => todo!("CM+GC"),
1879            DataModel::LinearMemory(opts) => opts,
1880        };
1881        let dst_mem_opts = match &dst_opts.data_model {
1882            DataModel::Gc {} => todo!("CM+GC"),
1883            DataModel::LinearMemory(opts) => opts,
1884        };
1885
1886        self.validate_string_length(src, src_enc);
1887
1888        // Optimistically assume that the code unit length of the source is
1889        // all that's needed in the destination. Perform that allocation
1890        // here and proceed to transcoding below.
1891        self.convert_src_len_to_dst(
1892            src.len.idx,
1893            src.opts.data_model.unwrap_memory().ptr(),
1894            dst_opts.data_model.unwrap_memory().ptr(),
1895        );
1896        let dst_len = self.local_tee_new_tmp(dst_opts.data_model.unwrap_memory().ptr());
1897        let dst_byte_len = self.local_set_new_tmp(dst_opts.data_model.unwrap_memory().ptr());
1898
1899        let dst = {
1900            let dst_mem = self.malloc(dst_opts, MallocSize::Local(dst_byte_len.idx), 1);
1901            WasmString {
1902                ptr: dst_mem.addr,
1903                len: dst_len,
1904                opts: dst_opts,
1905            }
1906        };
1907
1908        // Ensure buffers are all in-bounds
1909        let mut src_byte_len_tmp = None;
1910        let src_byte_len = match src_enc {
1911            FE::Latin1 => src.len.idx,
1912            FE::Utf16 => {
1913                self.instruction(LocalGet(src.len.idx));
1914                self.ptr_uconst(src_mem_opts, 1);
1915                self.ptr_shl(src_mem_opts);
1916                let tmp = self.local_set_new_tmp(src.opts.data_model.unwrap_memory().ptr());
1917                let ret = tmp.idx;
1918                src_byte_len_tmp = Some(tmp);
1919                ret
1920            }
1921            FE::Utf8 => unreachable!(),
1922        };
1923        self.validate_string_inbounds(src, src_byte_len);
1924        self.validate_string_inbounds(&dst, dst_byte_len.idx);
1925
1926        // Perform the initial transcode
1927        let op = match src_enc {
1928            FE::Latin1 => Transcode::Latin1ToUtf8,
1929            FE::Utf16 => Transcode::Utf16ToUtf8,
1930            FE::Utf8 => unreachable!(),
1931        };
1932        let transcode = self.transcoder(src, &dst, op);
1933        self.instruction(LocalGet(src.ptr.idx));
1934        self.instruction(LocalGet(src.len.idx));
1935        self.instruction(LocalGet(dst.ptr.idx));
1936        self.instruction(LocalGet(dst_byte_len.idx));
1937        self.instruction(Call(transcode.as_u32()));
1938        self.instruction(LocalSet(dst.len.idx));
1939        let src_len_tmp = self.local_set_new_tmp(src.opts.data_model.unwrap_memory().ptr());
1940
1941        // Test if the source was entirely transcoded by comparing
1942        // `src_len_tmp`, the number of code units transcoded from the
1943        // source, with `src_len`, the original number of code units.
1944        self.instruction(LocalGet(src_len_tmp.idx));
1945        self.instruction(LocalGet(src.len.idx));
1946        self.ptr_ne(src_mem_opts);
1947        self.instruction(If(BlockType::Empty));
1948
1949        // Here a worst-case reallocation is performed to grow `dst_mem`.
1950        // In-line a check is also performed that the worst-case byte size
1951        // fits within the maximum size of strings.
1952        self.instruction(LocalGet(dst.ptr.idx)); // old_ptr
1953        self.instruction(LocalGet(dst_byte_len.idx)); // old_size
1954        self.ptr_uconst(dst_mem_opts, 1); // align
1955        let factor = match src_enc {
1956            FE::Latin1 => 2,
1957            FE::Utf16 => 3,
1958            _ => unreachable!(),
1959        };
1960        self.validate_string_length_u8(src, factor);
1961        self.convert_src_len_to_dst(
1962            src.len.idx,
1963            src.opts.data_model.unwrap_memory().ptr(),
1964            dst_opts.data_model.unwrap_memory().ptr(),
1965        );
1966        self.ptr_uconst(dst_mem_opts, factor.into());
1967        self.ptr_mul(dst_mem_opts);
1968        self.instruction(LocalTee(dst_byte_len.idx));
1969        self.instruction(Call(dst_mem_opts.realloc.unwrap().as_u32()));
1970        self.instruction(LocalSet(dst.ptr.idx));
1971
1972        // Verify that the destination is still in-bounds
1973        self.validate_string_inbounds(&dst, dst_byte_len.idx);
1974
1975        // Perform another round of transcoding that should be guaranteed
1976        // to succeed. Note that all the parameters here are offset by the
1977        // results of the first transcoding to only perform the remaining
1978        // transcode on the final units.
1979        self.instruction(LocalGet(src.ptr.idx));
1980        self.instruction(LocalGet(src_len_tmp.idx));
1981        if let FE::Utf16 = src_enc {
1982            self.ptr_uconst(src_mem_opts, 1);
1983            self.ptr_shl(src_mem_opts);
1984        }
1985        self.ptr_add(src_mem_opts);
1986        self.instruction(LocalGet(src.len.idx));
1987        self.instruction(LocalGet(src_len_tmp.idx));
1988        self.ptr_sub(src_mem_opts);
1989        self.instruction(LocalGet(dst.ptr.idx));
1990        self.instruction(LocalGet(dst.len.idx));
1991        self.ptr_add(dst_mem_opts);
1992        self.instruction(LocalGet(dst_byte_len.idx));
1993        self.instruction(LocalGet(dst.len.idx));
1994        self.ptr_sub(dst_mem_opts);
1995        self.instruction(Call(transcode.as_u32()));
1996
1997        // Add the second result, the amount of destination units encoded,
1998        // to `dst_len` so it's an accurate reflection of the final size of
1999        // the destination buffer.
2000        self.instruction(LocalGet(dst.len.idx));
2001        self.ptr_add(dst_mem_opts);
2002        self.instruction(LocalSet(dst.len.idx));
2003
2004        // In debug mode verify the first result consumed the entire string,
2005        // otherwise simply discard it.
2006        if self.module.tunables.debug_adapter_modules {
2007            self.instruction(LocalGet(src.len.idx));
2008            self.instruction(LocalGet(src_len_tmp.idx));
2009            self.ptr_sub(src_mem_opts);
2010            self.ptr_ne(src_mem_opts);
2011            self.instruction(If(BlockType::Empty));
2012            self.trap(Trap::DebugAssertStringEncodingFinished);
2013            self.instruction(End);
2014        } else {
2015            self.instruction(Drop);
2016        }
2017
2018        // Perform a downsizing if the worst-case size was too large
2019        self.instruction(LocalGet(dst.len.idx));
2020        self.instruction(LocalGet(dst_byte_len.idx));
2021        self.ptr_ne(dst_mem_opts);
2022        self.instruction(If(BlockType::Empty));
2023        self.instruction(LocalGet(dst.ptr.idx)); // old_ptr
2024        self.instruction(LocalGet(dst_byte_len.idx)); // old_size
2025        self.ptr_uconst(dst_mem_opts, 1); // align
2026        self.instruction(LocalGet(dst.len.idx)); // new_size
2027        self.instruction(Call(dst_mem_opts.realloc.unwrap().as_u32()));
2028        self.instruction(LocalSet(dst.ptr.idx));
2029        self.instruction(End);
2030
2031        // If the first transcode was enough then assert that the returned
2032        // amount of destination items written equals the byte size.
2033        if self.module.tunables.debug_adapter_modules {
2034            self.instruction(Else);
2035
2036            self.instruction(LocalGet(dst.len.idx));
2037            self.instruction(LocalGet(dst_byte_len.idx));
2038            self.ptr_ne(dst_mem_opts);
2039            self.instruction(If(BlockType::Empty));
2040            self.trap(Trap::DebugAssertStringEncodingFinished);
2041            self.instruction(End);
2042        }
2043
2044        self.instruction(End); // end of "first transcode not enough"
2045
2046        self.free_temp_local(src_len_tmp);
2047        self.free_temp_local(dst_byte_len);
2048        if let Some(tmp) = src_byte_len_tmp {
2049            self.free_temp_local(tmp);
2050        }
2051
2052        dst
2053    }
2054
2055    // Corresponds to the `store_utf8_to_utf16` function in the spec.
2056    //
2057    // When converting utf-8 to utf-16 a pessimistic allocation is
2058    // done which is twice the byte length of the utf-8 string.
2059    // The host then transcodes and returns how many code units were
2060    // actually used during the transcoding and if it's beneath the
2061    // pessimistic maximum then the buffer is reallocated down to
2062    // a smaller amount.
2063    //
2064    // The host-imported transcoding function takes the src/dst pointer as
2065    // well as the code unit size of both the source and destination. The
2066    // destination should always be big enough to hold the result of the
2067    // transcode and so the result of the host function is how many code
2068    // units were written to the destination.
2069    fn string_utf8_to_utf16<'c>(
2070        &mut self,
2071        src: &WasmString<'_>,
2072        dst_opts: &'c Options,
2073    ) -> WasmString<'c> {
2074        let src_mem_opts = match &src.opts.data_model {
2075            DataModel::Gc {} => todo!("CM+GC"),
2076            DataModel::LinearMemory(opts) => opts,
2077        };
2078        let dst_mem_opts = match &dst_opts.data_model {
2079            DataModel::Gc {} => todo!("CM+GC"),
2080            DataModel::LinearMemory(opts) => opts,
2081        };
2082
2083        self.validate_string_length(src, FE::Utf16);
2084        self.convert_src_len_to_dst(
2085            src.len.idx,
2086            src_mem_opts.ptr(),
2087            dst_opts.data_model.unwrap_memory().ptr(),
2088        );
2089        let dst_len = self.local_tee_new_tmp(dst_opts.data_model.unwrap_memory().ptr());
2090        self.ptr_uconst(dst_mem_opts, 1);
2091        self.ptr_shl(dst_mem_opts);
2092        let dst_byte_len = self.local_set_new_tmp(dst_opts.data_model.unwrap_memory().ptr());
2093        let dst = {
2094            let dst_mem = self.malloc(dst_opts, MallocSize::Local(dst_byte_len.idx), 2);
2095            WasmString {
2096                ptr: dst_mem.addr,
2097                len: dst_len,
2098                opts: dst_opts,
2099            }
2100        };
2101
2102        self.validate_string_inbounds(src, src.len.idx);
2103        self.validate_string_inbounds(&dst, dst_byte_len.idx);
2104
2105        let transcode = self.transcoder(src, &dst, Transcode::Utf8ToUtf16);
2106        self.instruction(LocalGet(src.ptr.idx));
2107        self.instruction(LocalGet(src.len.idx));
2108        self.instruction(LocalGet(dst.ptr.idx));
2109        self.instruction(Call(transcode.as_u32()));
2110        self.instruction(LocalSet(dst.len.idx));
2111
2112        // If the number of code units returned by transcode is not
2113        // equal to the original number of code units then
2114        // the buffer must be shrunk.
2115        //
2116        // Note that the byte length of the final allocation we
2117        // want is twice the code unit length returned by the
2118        // transcoding function.
2119        self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2120        self.instruction(LocalGet(dst.len.idx));
2121        self.ptr_ne(dst_mem_opts);
2122        self.instruction(If(BlockType::Empty));
2123        self.instruction(LocalGet(dst.ptr.idx));
2124        self.instruction(LocalGet(dst_byte_len.idx));
2125        self.ptr_uconst(dst_mem_opts, 2);
2126        self.instruction(LocalGet(dst.len.idx));
2127        self.ptr_uconst(dst_mem_opts, 1);
2128        self.ptr_shl(dst_mem_opts);
2129        self.instruction(Call(match dst.opts.data_model {
2130            DataModel::Gc {} => todo!("CM+GC"),
2131            DataModel::LinearMemory(LinearMemoryOptions { realloc, .. }) => {
2132                realloc.unwrap().as_u32()
2133            }
2134        }));
2135        self.instruction(LocalSet(dst.ptr.idx));
2136        self.instruction(End); // end of shrink-to-fit
2137
2138        self.free_temp_local(dst_byte_len);
2139
2140        dst
2141    }
2142
2143    // Corresponds to `store_probably_utf16_to_latin1_or_utf16` in the spec.
2144    //
2145    // This will try to transcode the input utf16 string to utf16 in the
2146    // destination. If utf16 isn't needed though and latin1 could be used
2147    // then that's used instead and a reallocation to downsize occurs
2148    // afterwards.
2149    //
2150    // The host transcode function here will take the src/dst pointers as
2151    // well as src length. The destination byte length is twice the src code
2152    // unit length. The return value is the tagged length of the returned
2153    // string. If the upper bit is set then utf16 was used and the
2154    // conversion is done. If the upper bit is not set then latin1 was used
2155    // and a downsizing needs to happen.
2156    fn string_compact_utf16_to_compact<'c>(
2157        &mut self,
2158        src: &WasmString<'_>,
2159        dst_opts: &'c Options,
2160    ) -> WasmString<'c> {
2161        let src_mem_opts = match &src.opts.data_model {
2162            DataModel::Gc {} => todo!("CM+GC"),
2163            DataModel::LinearMemory(opts) => opts,
2164        };
2165        let dst_mem_opts = match &dst_opts.data_model {
2166            DataModel::Gc {} => todo!("CM+GC"),
2167            DataModel::LinearMemory(opts) => opts,
2168        };
2169
2170        self.validate_string_length(src, FE::Utf16);
2171        self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2172        let dst_len = self.local_tee_new_tmp(dst_mem_opts.ptr());
2173        self.ptr_uconst(dst_mem_opts, 1);
2174        self.ptr_shl(dst_mem_opts);
2175        let dst_byte_len = self.local_set_new_tmp(dst_mem_opts.ptr());
2176        let dst = {
2177            let dst_mem = self.malloc(dst_opts, MallocSize::Local(dst_byte_len.idx), 2);
2178            WasmString {
2179                ptr: dst_mem.addr,
2180                len: dst_len,
2181                opts: dst_opts,
2182            }
2183        };
2184
2185        self.convert_src_len_to_dst(
2186            dst_byte_len.idx,
2187            dst.opts.data_model.unwrap_memory().ptr(),
2188            src_mem_opts.ptr(),
2189        );
2190        let src_byte_len = self.local_set_new_tmp(src_mem_opts.ptr());
2191
2192        self.validate_string_inbounds(src, src_byte_len.idx);
2193        self.validate_string_inbounds(&dst, dst_byte_len.idx);
2194
2195        let transcode = self.transcoder(src, &dst, Transcode::Utf16ToCompactProbablyUtf16);
2196        self.instruction(LocalGet(src.ptr.idx));
2197        self.instruction(LocalGet(src.len.idx));
2198        self.instruction(LocalGet(dst.ptr.idx));
2199        self.instruction(Call(transcode.as_u32()));
2200        self.instruction(LocalSet(dst.len.idx));
2201
2202        // Assert that the untagged code unit length is the same as the
2203        // source code unit length.
2204        if self.module.tunables.debug_adapter_modules {
2205            self.instruction(LocalGet(dst.len.idx));
2206            self.ptr_uconst(dst_mem_opts, !UTF16_TAG);
2207            self.ptr_and(dst_mem_opts);
2208            self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2209            self.ptr_ne(dst_mem_opts);
2210            self.instruction(If(BlockType::Empty));
2211            self.trap(Trap::DebugAssertEqualCodeUnits);
2212            self.instruction(End);
2213        }
2214
2215        // If the UTF16_TAG is set then utf16 was used and the destination
2216        // should be appropriately sized. Bail out of the "is this string
2217        // empty" block and fall through otherwise to resizing.
2218        self.instruction(LocalGet(dst.len.idx));
2219        self.ptr_uconst(dst_mem_opts, UTF16_TAG);
2220        self.ptr_and(dst_mem_opts);
2221        self.ptr_br_if(dst_mem_opts, 0);
2222
2223        // Here `realloc` is used to downsize the string
2224        self.instruction(LocalGet(dst.ptr.idx)); // old_ptr
2225        self.instruction(LocalGet(dst_byte_len.idx)); // old_size
2226        self.ptr_uconst(dst_mem_opts, 2); // align
2227        self.instruction(LocalGet(dst.len.idx)); // new_size
2228        self.instruction(Call(dst_mem_opts.realloc.unwrap().as_u32()));
2229        self.instruction(LocalSet(dst.ptr.idx));
2230
2231        self.free_temp_local(dst_byte_len);
2232        self.free_temp_local(src_byte_len);
2233
2234        dst
2235    }
2236
2237    // Corresponds to `store_string_to_latin1_or_utf16` in the spec.
2238    //
2239    // This will attempt a first pass of transcoding to latin1 and on
2240    // failure a larger buffer is allocated for utf16 and then utf16 is
2241    // encoded in-place into the buffer. After either latin1 or utf16 the
2242    // buffer is then resized to fit the final string allocation.
2243    fn string_to_compact<'c>(
2244        &mut self,
2245        src: &WasmString<'_>,
2246        src_enc: FE,
2247        dst_opts: &'c Options,
2248    ) -> WasmString<'c> {
2249        let src_mem_opts = match &src.opts.data_model {
2250            DataModel::Gc {} => todo!("CM+GC"),
2251            DataModel::LinearMemory(opts) => opts,
2252        };
2253        let dst_mem_opts = match &dst_opts.data_model {
2254            DataModel::Gc {} => todo!("CM+GC"),
2255            DataModel::LinearMemory(opts) => opts,
2256        };
2257
2258        self.validate_string_length(src, src_enc);
2259        self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2260        let dst_len = self.local_tee_new_tmp(dst_mem_opts.ptr());
2261        let dst_byte_len = self.local_set_new_tmp(dst_mem_opts.ptr());
2262        let dst = {
2263            let dst_mem = self.malloc(dst_opts, MallocSize::Local(dst_byte_len.idx), 2);
2264            WasmString {
2265                ptr: dst_mem.addr,
2266                len: dst_len,
2267                opts: dst_opts,
2268            }
2269        };
2270
2271        self.validate_string_inbounds(src, src.len.idx);
2272        self.validate_string_inbounds(&dst, dst_byte_len.idx);
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.instruction(LocalGet(dst.ptr.idx)); // old_ptr
2306        self.instruction(LocalGet(dst_byte_len.idx)); // old_size
2307        self.ptr_uconst(dst_mem_opts, 2); // align
2308        self.instruction(LocalGet(dst.len.idx)); // new_size
2309        self.instruction(Call(dst_mem_opts.realloc.unwrap().as_u32()));
2310        self.instruction(LocalSet(dst.ptr.idx));
2311        self.instruction(End);
2312
2313        // In this block the latin1 encoding failed. The host transcode
2314        // returned how many units were consumed from the source and how
2315        // many bytes were written to the destination. Here the buffer is
2316        // inflated and sized and the second utf16 intrinsic is invoked to
2317        // perform the final inflation.
2318        self.instruction(Else); // else latin1-or-utf16 block
2319
2320        // For utf8 validate that the inflated size is still within bounds.
2321        if src_enc.width() == 1 {
2322            self.validate_string_length_u8(src, 2);
2323        }
2324
2325        // Reallocate the buffer with twice the source code units in byte
2326        // size.
2327        self.instruction(LocalGet(dst.ptr.idx)); // old_ptr
2328        self.instruction(LocalGet(dst_byte_len.idx)); // old_size
2329        self.ptr_uconst(dst_mem_opts, 2); // align
2330        self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2331        self.ptr_uconst(dst_mem_opts, 1);
2332        self.ptr_shl(dst_mem_opts);
2333        self.instruction(LocalTee(dst_byte_len.idx));
2334        self.instruction(Call(dst_mem_opts.realloc.unwrap().as_u32()));
2335        self.instruction(LocalSet(dst.ptr.idx));
2336
2337        // Call the host utf16 transcoding function. This will inflate the
2338        // prior latin1 bytes and then encode the rest of the source string
2339        // as utf16 into the remaining space in the destination buffer.
2340        self.instruction(LocalGet(src.ptr.idx));
2341        self.instruction(LocalGet(src_len_tmp.idx));
2342        if let FE::Utf16 = src_enc {
2343            self.ptr_uconst(src_mem_opts, 1);
2344            self.ptr_shl(src_mem_opts);
2345        }
2346        self.ptr_add(src_mem_opts);
2347        self.instruction(LocalGet(src.len.idx));
2348        self.instruction(LocalGet(src_len_tmp.idx));
2349        self.ptr_sub(src_mem_opts);
2350        self.instruction(LocalGet(dst.ptr.idx));
2351        self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2352        self.instruction(LocalGet(dst.len.idx));
2353        self.instruction(Call(transcode_utf16.as_u32()));
2354        self.instruction(LocalSet(dst.len.idx));
2355
2356        // If the returned number of code units written to the destination
2357        // is not equal to the size of the allocation then the allocation is
2358        // resized down to the appropriate size.
2359        //
2360        // Note that the byte size desired is `2*dst_len` and the current
2361        // byte buffer size is `2*src_len` so the `2` factor isn't checked
2362        // here, just the lengths.
2363        self.instruction(LocalGet(dst.len.idx));
2364        self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2365        self.ptr_ne(dst_mem_opts);
2366        self.instruction(If(BlockType::Empty));
2367        self.instruction(LocalGet(dst.ptr.idx)); // old_ptr
2368        self.instruction(LocalGet(dst_byte_len.idx)); // old_size
2369        self.ptr_uconst(dst_mem_opts, 2); // align
2370        self.instruction(LocalGet(dst.len.idx));
2371        self.ptr_uconst(dst_mem_opts, 1);
2372        self.ptr_shl(dst_mem_opts);
2373        self.instruction(Call(dst_mem_opts.realloc.unwrap().as_u32()));
2374        self.instruction(LocalSet(dst.ptr.idx));
2375        self.instruction(End);
2376
2377        // Tag the returned pointer as utf16
2378        self.instruction(LocalGet(dst.len.idx));
2379        self.ptr_uconst(dst_mem_opts, UTF16_TAG);
2380        self.ptr_or(dst_mem_opts);
2381        self.instruction(LocalSet(dst.len.idx));
2382
2383        self.instruction(End); // end latin1-or-utf16 block
2384
2385        self.free_temp_local(src_len_tmp);
2386        self.free_temp_local(dst_byte_len);
2387
2388        dst
2389    }
2390
2391    fn validate_string_length(&mut self, src: &WasmString<'_>, dst: FE) {
2392        self.validate_string_length_u8(src, dst.width())
2393    }
2394
2395    fn validate_string_length_u8(&mut self, s: &WasmString<'_>, dst: u8) {
2396        let mem_opts = match &s.opts.data_model {
2397            DataModel::Gc {} => todo!("CM+GC"),
2398            DataModel::LinearMemory(opts) => opts,
2399        };
2400
2401        // Check to see if the source byte length is out of bounds in
2402        // which case a trap is generated.
2403        self.instruction(LocalGet(s.len.idx));
2404        let max = MAX_STRING_BYTE_LENGTH / u32::from(dst);
2405        self.ptr_uconst(mem_opts, max);
2406        self.ptr_ge_u(mem_opts);
2407        self.instruction(If(BlockType::Empty));
2408        self.trap(Trap::StringOutOfBounds);
2409        self.instruction(End);
2410    }
2411
2412    fn transcoder(
2413        &mut self,
2414        src: &WasmString<'_>,
2415        dst: &WasmString<'_>,
2416        op: Transcode,
2417    ) -> FuncIndex {
2418        match (src.opts.data_model, dst.opts.data_model) {
2419            (DataModel::Gc {}, _) | (_, DataModel::Gc {}) => {
2420                todo!("CM+GC")
2421            }
2422            (
2423                DataModel::LinearMemory(LinearMemoryOptions {
2424                    memory64: src64,
2425                    memory: src_mem,
2426                    realloc: _,
2427                }),
2428                DataModel::LinearMemory(LinearMemoryOptions {
2429                    memory64: dst64,
2430                    memory: dst_mem,
2431                    realloc: _,
2432                }),
2433            ) => self.module.import_transcoder(Transcoder {
2434                from_memory: src_mem.unwrap(),
2435                from_memory64: src64,
2436                to_memory: dst_mem.unwrap(),
2437                to_memory64: dst64,
2438                op,
2439            }),
2440        }
2441    }
2442
2443    fn validate_string_inbounds(&mut self, s: &WasmString<'_>, byte_len: u32) {
2444        match &s.opts.data_model {
2445            DataModel::Gc {} => todo!("CM+GC"),
2446            DataModel::LinearMemory(opts) => {
2447                self.validate_memory_inbounds(opts, s.ptr.idx, byte_len, Trap::StringOutOfBounds)
2448            }
2449        }
2450    }
2451
2452    fn validate_memory_inbounds(
2453        &mut self,
2454        opts: &LinearMemoryOptions,
2455        ptr_local: u32,
2456        byte_len_local: u32,
2457        trap: Trap,
2458    ) {
2459        let extend_to_64 = |me: &mut Self| {
2460            if !opts.memory64 {
2461                me.instruction(I64ExtendI32U);
2462            }
2463        };
2464
2465        self.instruction(Block(BlockType::Empty));
2466        self.instruction(Block(BlockType::Empty));
2467
2468        // Calculate the full byte size of memory with `memory.size`. Note that
2469        // arithmetic here is done always in 64-bits to accommodate 4G memories.
2470        // Additionally it's assumed that 64-bit memories never fill up
2471        // entirely.
2472        self.instruction(MemorySize(opts.memory.unwrap().as_u32()));
2473        extend_to_64(self);
2474        self.instruction(I64Const(16));
2475        self.instruction(I64Shl);
2476
2477        // Calculate the end address of the string. This is done by adding the
2478        // base pointer to the byte length. For 32-bit memories there's no need
2479        // to check for overflow since everything is extended to 64-bit, but for
2480        // 64-bit memories overflow is checked.
2481        self.instruction(LocalGet(ptr_local));
2482        extend_to_64(self);
2483        self.instruction(LocalGet(byte_len_local));
2484        extend_to_64(self);
2485        self.instruction(I64Add);
2486        if opts.memory64 {
2487            let tmp = self.local_tee_new_tmp(ValType::I64);
2488            self.instruction(LocalGet(ptr_local));
2489            self.ptr_lt_u(opts);
2490            self.instruction(BrIf(0));
2491            self.instruction(LocalGet(tmp.idx));
2492            self.free_temp_local(tmp);
2493        }
2494
2495        // If the byte size of memory is greater than the final address of the
2496        // string then the string is invalid. Note that if it's precisely equal
2497        // then that's ok.
2498        self.instruction(I64GeU);
2499        self.instruction(BrIf(1));
2500
2501        self.instruction(End);
2502        self.trap(trap);
2503        self.instruction(End);
2504    }
2505
2506    /// Shared preamble for translating list-like sequences (lists and maps).
2507    ///
2508    /// Emits: load ptr/len from source, compute byte lengths, malloc
2509    /// destination, validate bounds, and if element sizes are non-zero opens
2510    /// Block + Loop and initializes iteration locals.
2511    ///
2512    /// Returns a `SequenceTranslation` that the caller uses to emit the
2513    /// loop body before calling `end_translate_sequence`.
2514    fn begin_translate_sequence<'c>(
2515        &mut self,
2516        src: &Source<'c>,
2517        dst: &Destination<'c>,
2518        src_element_size: u32,
2519        src_element_align: u32,
2520        dst_element_size: u32,
2521        dst_element_align: u32,
2522    ) -> SequenceTranslation<'c> {
2523        let src_mem_opts = match &src.opts().data_model {
2524            DataModel::Gc {} => todo!("CM+GC"),
2525            DataModel::LinearMemory(opts) => opts,
2526        };
2527        let dst_mem_opts = match &dst.opts().data_model {
2528            DataModel::Gc {} => todo!("CM+GC"),
2529            DataModel::LinearMemory(opts) => opts,
2530        };
2531
2532        let src_opts = src.opts();
2533        let dst_opts = dst.opts();
2534
2535        // Load the pointer/length of this sequence into temporary locals.
2536        // These will be referenced a good deal so this just makes it easier
2537        // to deal with them consistently below rather than trying to reload
2538        // from memory for example.
2539        match src {
2540            Source::Stack(s) => {
2541                assert_eq!(s.locals.len(), 2);
2542                self.stack_get(&s.slice(0..1), src_mem_opts.ptr());
2543                self.stack_get(&s.slice(1..2), src_mem_opts.ptr());
2544            }
2545            Source::Memory(mem) => {
2546                self.ptr_load(mem);
2547                self.ptr_load(&mem.bump(src_mem_opts.ptr_size().into()));
2548            }
2549            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
2550        }
2551        let src_len = self.local_set_new_tmp(src_mem_opts.ptr());
2552        let src_ptr = self.local_set_new_tmp(src_mem_opts.ptr());
2553
2554        // Create a `Memory` operand which will internally assert that the
2555        // `src_ptr` value is properly aligned.
2556        let src_mem = self.memory_operand(src_opts, src_ptr, src_element_align);
2557
2558        // Calculate the source/destination byte lengths into unique locals.
2559        let src_byte_len =
2560            self.calculate_list_byte_len(src_mem_opts, src_len.idx, src_element_size);
2561        let dst_byte_len = if src_element_size == dst_element_size {
2562            self.convert_src_len_to_dst(src_byte_len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2563            self.local_set_new_tmp(dst_mem_opts.ptr())
2564        } else if src_mem_opts.ptr() == dst_mem_opts.ptr() {
2565            self.calculate_list_byte_len(dst_mem_opts, src_len.idx, dst_element_size)
2566        } else {
2567            self.convert_src_len_to_dst(src_byte_len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2568            let tmp = self.local_set_new_tmp(dst_mem_opts.ptr());
2569            let ret = self.calculate_list_byte_len(dst_mem_opts, tmp.idx, dst_element_size);
2570            self.free_temp_local(tmp);
2571            ret
2572        };
2573
2574        // Here `realloc` is invoked (in a `malloc`-like fashion) to allocate
2575        // space for the sequence in the destination memory. This will also
2576        // internally insert checks that the returned pointer is aligned
2577        // correctly for the destination.
2578        let dst_mem = self.malloc(
2579            dst_opts,
2580            MallocSize::Local(dst_byte_len.idx),
2581            dst_element_align,
2582        );
2583
2584        // With all the pointers and byte lengths verify that both the source
2585        // and the destination buffers are in-bounds.
2586        self.validate_memory_inbounds(
2587            src_mem_opts,
2588            src_mem.addr.idx,
2589            src_byte_len.idx,
2590            Trap::ListOutOfBounds,
2591        );
2592        self.validate_memory_inbounds(
2593            dst_mem_opts,
2594            dst_mem.addr.idx,
2595            dst_byte_len.idx,
2596            Trap::ListOutOfBounds,
2597        );
2598
2599        self.free_temp_local(src_byte_len);
2600        self.free_temp_local(dst_byte_len);
2601
2602        // If both element sizes are 0 then there's nothing to copy so the
2603        // loop is skipped entirely. Otherwise open a Block (for early exit
2604        // on zero-length) and a Loop for the per-element iteration.
2605        let loop_state = if src_element_size > 0 || dst_element_size > 0 {
2606            self.instruction(Block(BlockType::Empty));
2607
2608            // Set the `remaining` local and only continue if it's > 0.
2609            self.instruction(LocalGet(src_len.idx));
2610            let remaining = self.local_tee_new_tmp(src_mem_opts.ptr());
2611            self.ptr_eqz(src_mem_opts);
2612            self.instruction(BrIf(0));
2613
2614            // Initialize the two iteration pointers to their starting values.
2615            self.instruction(LocalGet(src_mem.addr.idx));
2616            let cur_src_ptr = self.local_set_new_tmp(src_mem_opts.ptr());
2617            self.instruction(LocalGet(dst_mem.addr.idx));
2618            let cur_dst_ptr = self.local_set_new_tmp(dst_mem_opts.ptr());
2619
2620            self.instruction(Loop(BlockType::Empty));
2621
2622            Some(SequenceLoopState {
2623                remaining,
2624                cur_src_ptr,
2625                cur_dst_ptr,
2626            })
2627        } else {
2628            None
2629        };
2630
2631        SequenceTranslation {
2632            src_len,
2633            src_mem,
2634            dst_mem,
2635            src_opts,
2636            dst_opts,
2637            src_mem_opts,
2638            dst_mem_opts,
2639            loop_state,
2640        }
2641    }
2642
2643    /// Shared epilogue for translating list-like sequences.
2644    ///
2645    /// If a loop was opened, emits: decrement remaining, BrIf to loop
2646    /// head, End loop, End block, and frees iteration locals. Then stores
2647    /// the ptr/len pair into the destination and frees all temporaries.
2648    fn end_translate_sequence(&mut self, seq: SequenceTranslation<'_>, dst: &Destination) {
2649        if let Some(loop_state) = seq.loop_state {
2650            // Update the remaining count, falling through to break out if
2651            // it's zero now.
2652            self.instruction(LocalGet(loop_state.remaining.idx));
2653            self.ptr_iconst(seq.src_mem_opts, -1);
2654            self.ptr_add(seq.src_mem_opts);
2655            self.instruction(LocalTee(loop_state.remaining.idx));
2656            self.ptr_br_if(seq.src_mem_opts, 0);
2657            self.instruction(End); // end of loop
2658            self.instruction(End); // end of block
2659
2660            self.free_temp_local(loop_state.cur_dst_ptr);
2661            self.free_temp_local(loop_state.cur_src_ptr);
2662            self.free_temp_local(loop_state.remaining);
2663        }
2664
2665        // Store the ptr/length in the desired destination.
2666        match dst {
2667            Destination::Stack(s, _) => {
2668                self.instruction(LocalGet(seq.dst_mem.addr.idx));
2669                self.stack_set(&s[..1], seq.dst_mem_opts.ptr());
2670                self.convert_src_len_to_dst(
2671                    seq.src_len.idx,
2672                    seq.src_mem_opts.ptr(),
2673                    seq.dst_mem_opts.ptr(),
2674                );
2675                self.stack_set(&s[1..], seq.dst_mem_opts.ptr());
2676            }
2677            Destination::Memory(mem) => {
2678                self.instruction(LocalGet(mem.addr.idx));
2679                self.instruction(LocalGet(seq.dst_mem.addr.idx));
2680                self.ptr_store(mem);
2681                self.instruction(LocalGet(mem.addr.idx));
2682                self.convert_src_len_to_dst(
2683                    seq.src_len.idx,
2684                    seq.src_mem_opts.ptr(),
2685                    seq.dst_mem_opts.ptr(),
2686                );
2687                self.ptr_store(&mem.bump(seq.dst_mem_opts.ptr_size().into()));
2688            }
2689            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
2690        }
2691
2692        self.free_temp_local(seq.src_len);
2693        self.free_temp_local(seq.src_mem.addr);
2694        self.free_temp_local(seq.dst_mem.addr);
2695    }
2696
2697    fn translate_list(
2698        &mut self,
2699        src_ty: TypeListIndex,
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_element_ty = &self.types[src_ty].element;
2714        let dst_element_ty = match dst_ty {
2715            InterfaceType::List(r) => &self.types[*r].element,
2716            _ => panic!("expected a list"),
2717        };
2718        let (src_size, src_align) = self.types.size_align(src_mem_opts, src_element_ty);
2719        let (dst_size, dst_align) = self.types.size_align(dst_mem_opts, dst_element_ty);
2720
2721        let seq = self.begin_translate_sequence(src, dst, src_size, src_align, dst_size, dst_align);
2722
2723        if let Some(ref loop_state) = seq.loop_state {
2724            let element_src = Source::Memory(Memory {
2725                opts: seq.src_opts,
2726                offset: 0,
2727                addr: TempLocal::new(loop_state.cur_src_ptr.idx, loop_state.cur_src_ptr.ty),
2728            });
2729            let element_dst = Destination::Memory(Memory {
2730                opts: seq.dst_opts,
2731                offset: 0,
2732                addr: TempLocal::new(loop_state.cur_dst_ptr.idx, loop_state.cur_dst_ptr.ty),
2733            });
2734            self.translate(src_element_ty, &element_src, dst_element_ty, &element_dst);
2735
2736            if src_size > 0 {
2737                self.instruction(LocalGet(loop_state.cur_src_ptr.idx));
2738                self.ptr_uconst(src_mem_opts, src_size);
2739                self.ptr_add(src_mem_opts);
2740                self.instruction(LocalSet(loop_state.cur_src_ptr.idx));
2741            }
2742            if dst_size > 0 {
2743                self.instruction(LocalGet(loop_state.cur_dst_ptr.idx));
2744                self.ptr_uconst(dst_mem_opts, dst_size);
2745                self.ptr_add(dst_mem_opts);
2746                self.instruction(LocalSet(loop_state.cur_dst_ptr.idx));
2747            }
2748        }
2749
2750        self.end_translate_sequence(seq, dst);
2751    }
2752
2753    /// Translates a map from one component's memory to another.
2754    ///
2755    /// In the Component Model, a `map<K, V>` is stored in memory as
2756    /// `list<tuple<K, V>>`, so the translation reuses the same sequence
2757    /// scaffolding as lists but with a two-field (key, value) loop body.
2758    fn translate_map(
2759        &mut self,
2760        src_ty: TypeMapIndex,
2761        src: &Source<'_>,
2762        dst_ty: &InterfaceType,
2763        dst: &Destination,
2764    ) {
2765        let src_mem_opts = match &src.opts().data_model {
2766            DataModel::Gc {} => todo!("CM+GC"),
2767            DataModel::LinearMemory(opts) => opts,
2768        };
2769        let dst_mem_opts = match &dst.opts().data_model {
2770            DataModel::Gc {} => todo!("CM+GC"),
2771            DataModel::LinearMemory(opts) => opts,
2772        };
2773
2774        let src_map_ty = &self.types[src_ty];
2775        let dst_map_ty = match dst_ty {
2776            InterfaceType::Map(r) => &self.types[*r],
2777            _ => panic!("expected a map"),
2778        };
2779
2780        // Each map entry is a tuple<K, V> following record layout rules.
2781        let src_key_abi = self.types.canonical_abi(&src_map_ty.key);
2782        let src_value_abi = self.types.canonical_abi(&src_map_ty.value);
2783        let src_entry_abi = CanonicalAbiInfo::record([src_key_abi, src_value_abi].into_iter());
2784        let (_, src_key_align) = self.types.size_align(src_mem_opts, &src_map_ty.key);
2785        let (_, src_value_align) = self.types.size_align(src_mem_opts, &src_map_ty.value);
2786        let (src_tuple_size, src_entry_align) = if src_mem_opts.memory64 {
2787            (src_entry_abi.size64, src_entry_abi.align64)
2788        } else {
2789            (src_entry_abi.size32, src_entry_abi.align32)
2790        };
2791        let src_value_offset = {
2792            let mut offset = 0u32;
2793            if src_mem_opts.memory64 {
2794                src_key_abi.next_field64(&mut offset);
2795                src_value_abi.next_field64(&mut offset)
2796            } else {
2797                src_key_abi.next_field32(&mut offset);
2798                src_value_abi.next_field32(&mut offset)
2799            }
2800        };
2801
2802        let dst_key_abi = self.types.canonical_abi(&dst_map_ty.key);
2803        let dst_value_abi = self.types.canonical_abi(&dst_map_ty.value);
2804        let dst_entry_abi = CanonicalAbiInfo::record([dst_key_abi, dst_value_abi].into_iter());
2805        let (_, dst_key_align) = self.types.size_align(dst_mem_opts, &dst_map_ty.key);
2806        let (_, dst_value_align) = self.types.size_align(dst_mem_opts, &dst_map_ty.value);
2807        let (dst_tuple_size, dst_entry_align) = if dst_mem_opts.memory64 {
2808            (dst_entry_abi.size64, dst_entry_abi.align64)
2809        } else {
2810            (dst_entry_abi.size32, dst_entry_abi.align32)
2811        };
2812        let dst_value_offset = {
2813            let mut offset = 0u32;
2814            if dst_mem_opts.memory64 {
2815                dst_key_abi.next_field64(&mut offset);
2816                dst_value_abi.next_field64(&mut offset)
2817            } else {
2818                dst_key_abi.next_field32(&mut offset);
2819                dst_value_abi.next_field32(&mut offset)
2820            }
2821        };
2822
2823        let seq = self.begin_translate_sequence(
2824            src,
2825            dst,
2826            src_tuple_size,
2827            src_entry_align,
2828            dst_tuple_size,
2829            dst_entry_align,
2830        );
2831
2832        if let Some(ref loop_state) = seq.loop_state {
2833            let key_src = Source::Memory(self.memory_operand(
2834                seq.src_opts,
2835                TempLocal {
2836                    idx: loop_state.cur_src_ptr.idx,
2837                    ty: src_mem_opts.ptr(),
2838                    needs_free: false,
2839                },
2840                src_key_align,
2841            ));
2842            let key_dst = Destination::Memory(self.memory_operand(
2843                seq.dst_opts,
2844                TempLocal {
2845                    idx: loop_state.cur_dst_ptr.idx,
2846                    ty: dst_mem_opts.ptr(),
2847                    needs_free: false,
2848                },
2849                dst_key_align,
2850            ));
2851            self.translate(&src_map_ty.key, &key_src, &dst_map_ty.key, &key_dst);
2852
2853            if src_value_offset > 0 {
2854                self.instruction(LocalGet(loop_state.cur_src_ptr.idx));
2855                self.ptr_uconst(src_mem_opts, src_value_offset);
2856                self.ptr_add(src_mem_opts);
2857                self.instruction(LocalSet(loop_state.cur_src_ptr.idx));
2858            }
2859            if dst_value_offset > 0 {
2860                self.instruction(LocalGet(loop_state.cur_dst_ptr.idx));
2861                self.ptr_uconst(dst_mem_opts, dst_value_offset);
2862                self.ptr_add(dst_mem_opts);
2863                self.instruction(LocalSet(loop_state.cur_dst_ptr.idx));
2864            }
2865
2866            let value_src = Source::Memory(self.memory_operand(
2867                seq.src_opts,
2868                TempLocal {
2869                    idx: loop_state.cur_src_ptr.idx,
2870                    ty: src_mem_opts.ptr(),
2871                    needs_free: false,
2872                },
2873                src_value_align,
2874            ));
2875            let value_dst = Destination::Memory(self.memory_operand(
2876                seq.dst_opts,
2877                TempLocal {
2878                    idx: loop_state.cur_dst_ptr.idx,
2879                    ty: dst_mem_opts.ptr(),
2880                    needs_free: false,
2881                },
2882                dst_value_align,
2883            ));
2884            self.translate(&src_map_ty.value, &value_src, &dst_map_ty.value, &value_dst);
2885
2886            // Advance past value + trailing padding to the next entry
2887            let src_advance_to_next = src_tuple_size - src_value_offset;
2888            if src_advance_to_next > 0 {
2889                self.instruction(LocalGet(loop_state.cur_src_ptr.idx));
2890                self.ptr_uconst(src_mem_opts, src_advance_to_next);
2891                self.ptr_add(src_mem_opts);
2892                self.instruction(LocalSet(loop_state.cur_src_ptr.idx));
2893            }
2894            let dst_advance_to_next = dst_tuple_size - dst_value_offset;
2895            if dst_advance_to_next > 0 {
2896                self.instruction(LocalGet(loop_state.cur_dst_ptr.idx));
2897                self.ptr_uconst(dst_mem_opts, dst_advance_to_next);
2898                self.ptr_add(dst_mem_opts);
2899                self.instruction(LocalSet(loop_state.cur_dst_ptr.idx));
2900            }
2901        }
2902
2903        self.end_translate_sequence(seq, dst);
2904    }
2905
2906    fn calculate_list_byte_len(
2907        &mut self,
2908        opts: &LinearMemoryOptions,
2909        len_local: u32,
2910        elt_size: u32,
2911    ) -> TempLocal {
2912        // Zero-size types are easy to handle here because the byte size of the
2913        // destination is always zero.
2914        if elt_size == 0 {
2915            self.ptr_uconst(opts, 0);
2916            return self.local_set_new_tmp(opts.ptr());
2917        }
2918
2919        // For one-byte elements in the destination the check here can be a bit
2920        // more optimal than the general case below. In these situations if the
2921        // source pointer type is 32-bit then we're guaranteed to not overflow,
2922        // so the source length is simply casted to the destination's type.
2923        //
2924        // If the source is 64-bit then all that needs to be checked is to
2925        // ensure that it does not have the upper 32-bits set.
2926        if elt_size == 1 {
2927            if let ValType::I64 = opts.ptr() {
2928                self.instruction(LocalGet(len_local));
2929                self.instruction(I64Const(32));
2930                self.instruction(I64ShrU);
2931                self.instruction(I32WrapI64);
2932                self.instruction(If(BlockType::Empty));
2933                self.trap(Trap::ListOutOfBounds);
2934                self.instruction(End);
2935            }
2936            self.instruction(LocalGet(len_local));
2937            return self.local_set_new_tmp(opts.ptr());
2938        }
2939
2940        // The main check implemented by this function is to verify that
2941        // `src_len_local` does not exceed the 32-bit range. Byte sizes for
2942        // lists must always fit in 32-bits to get transferred to 32-bit
2943        // memories.
2944        self.instruction(Block(BlockType::Empty));
2945        self.instruction(Block(BlockType::Empty));
2946        self.instruction(LocalGet(len_local));
2947        match opts.ptr() {
2948            // The source's list length is guaranteed to be less than 32-bits
2949            // so simply extend it up to a 64-bit type for the multiplication
2950            // below.
2951            ValType::I32 => self.instruction(I64ExtendI32U),
2952
2953            // If the source is a 64-bit memory then if the item length doesn't
2954            // fit in 32-bits the byte length definitely won't, so generate a
2955            // branch to our overflow trap here if any of the upper 32-bits are set.
2956            ValType::I64 => {
2957                self.instruction(I64Const(32));
2958                self.instruction(I64ShrU);
2959                self.instruction(I32WrapI64);
2960                self.instruction(BrIf(0));
2961                self.instruction(LocalGet(len_local));
2962            }
2963
2964            _ => unreachable!(),
2965        }
2966
2967        // Next perform a 64-bit multiplication with the element byte size that
2968        // is itself guaranteed to fit in 32-bits. The result is then checked
2969        // to see if we overflowed the 32-bit space. The two input operands to
2970        // the multiplication are guaranteed to be 32-bits at most which means
2971        // that this multiplication shouldn't overflow.
2972        //
2973        // The result of the multiplication is saved into a local as well to
2974        // get the result afterwards.
2975        self.instruction(I64Const(elt_size.into()));
2976        self.instruction(I64Mul);
2977        let tmp = self.local_tee_new_tmp(ValType::I64);
2978        // Branch to success if the upper 32-bits are zero, otherwise
2979        // fall-through to the trap.
2980        self.instruction(I64Const(32));
2981        self.instruction(I64ShrU);
2982        self.instruction(I64Eqz);
2983        self.instruction(BrIf(1));
2984        self.instruction(End);
2985        self.trap(Trap::ListOutOfBounds);
2986        self.instruction(End);
2987
2988        // If a fresh local was used to store the result of the multiplication
2989        // then convert it down to 32-bits which should be guaranteed to not
2990        // lose information at this point.
2991        if opts.ptr() == ValType::I64 {
2992            tmp
2993        } else {
2994            self.instruction(LocalGet(tmp.idx));
2995            self.instruction(I32WrapI64);
2996            self.free_temp_local(tmp);
2997            self.local_set_new_tmp(ValType::I32)
2998        }
2999    }
3000
3001    fn convert_src_len_to_dst(
3002        &mut self,
3003        src_len_local: u32,
3004        src_ptr_ty: ValType,
3005        dst_ptr_ty: ValType,
3006    ) {
3007        self.instruction(LocalGet(src_len_local));
3008        match (src_ptr_ty, dst_ptr_ty) {
3009            (ValType::I32, ValType::I64) => self.instruction(I64ExtendI32U),
3010            (ValType::I64, ValType::I32) => self.instruction(I32WrapI64),
3011            (src, dst) => assert_eq!(src, dst),
3012        }
3013    }
3014
3015    fn translate_record(
3016        &mut self,
3017        src_ty: TypeRecordIndex,
3018        src: &Source<'_>,
3019        dst_ty: &InterfaceType,
3020        dst: &Destination,
3021    ) {
3022        let src_ty = &self.types[src_ty];
3023        let dst_ty = match dst_ty {
3024            InterfaceType::Record(r) => &self.types[*r],
3025            _ => panic!("expected a record"),
3026        };
3027
3028        // TODO: subtyping
3029        assert_eq!(src_ty.fields.len(), dst_ty.fields.len());
3030
3031        // First a map is made of the source fields to where they're coming
3032        // from (e.g. which offset or which locals). This map is keyed by the
3033        // fields' names
3034        let mut src_fields = HashMap::new();
3035        for (i, src) in src
3036            .record_field_srcs(self.types, src_ty.fields.iter().map(|f| f.ty))
3037            .enumerate()
3038        {
3039            let field = &src_ty.fields[i];
3040            src_fields.insert(&field.name, (src, &field.ty));
3041        }
3042
3043        // .. and next translation is performed in the order of the destination
3044        // fields in case the destination is the stack to ensure that the stack
3045        // has the fields all in the right order.
3046        //
3047        // Note that the lookup in `src_fields` is an infallible lookup which
3048        // will panic if the field isn't found.
3049        //
3050        // TODO: should that lookup be fallible with subtyping?
3051        for (i, dst) in dst
3052            .record_field_dsts(self.types, dst_ty.fields.iter().map(|f| f.ty))
3053            .enumerate()
3054        {
3055            let field = &dst_ty.fields[i];
3056            let (src, src_ty) = &src_fields[&field.name];
3057            self.translate(src_ty, src, &field.ty, &dst);
3058        }
3059    }
3060
3061    fn translate_flags(
3062        &mut self,
3063        src_ty: TypeFlagsIndex,
3064        src: &Source<'_>,
3065        dst_ty: &InterfaceType,
3066        dst: &Destination,
3067    ) {
3068        let src_ty = &self.types[src_ty];
3069        let dst_ty = match dst_ty {
3070            InterfaceType::Flags(r) => &self.types[*r],
3071            _ => panic!("expected a record"),
3072        };
3073
3074        // TODO: subtyping
3075        //
3076        // Notably this implementation does not support reordering flags from
3077        // the source to the destination nor having more flags in the
3078        // destination. Currently this is a copy from source to destination
3079        // in-bulk. Otherwise reordering indices would have to have some sort of
3080        // fancy bit twiddling tricks or something like that.
3081        assert_eq!(src_ty.names, dst_ty.names);
3082        let cnt = src_ty.names.len();
3083        match FlagsSize::from_count(cnt) {
3084            FlagsSize::Size0 => {}
3085            FlagsSize::Size1 => {
3086                let mask = if cnt == 8 { 0xff } else { (1 << cnt) - 1 };
3087                self.convert_u8_mask(src, dst, mask);
3088            }
3089            FlagsSize::Size2 => {
3090                let mask = if cnt == 16 { 0xffff } else { (1 << cnt) - 1 };
3091                self.convert_u16_mask(src, dst, mask);
3092            }
3093            FlagsSize::Size4Plus(n) => {
3094                let srcs = src.record_field_srcs(self.types, (0..n).map(|_| InterfaceType::U32));
3095                let dsts = dst.record_field_dsts(self.types, (0..n).map(|_| InterfaceType::U32));
3096                let n = usize::from(n);
3097                for (i, (src, dst)) in srcs.zip(dsts).enumerate() {
3098                    let mask = if i == n - 1 && (cnt % 32 != 0) {
3099                        (1 << (cnt % 32)) - 1
3100                    } else {
3101                        0xffffffff
3102                    };
3103                    self.convert_u32_mask(&src, &dst, mask);
3104                }
3105            }
3106        }
3107    }
3108
3109    fn translate_tuple(
3110        &mut self,
3111        src_ty: TypeTupleIndex,
3112        src: &Source<'_>,
3113        dst_ty: &InterfaceType,
3114        dst: &Destination,
3115    ) {
3116        let src_ty = &self.types[src_ty];
3117        let dst_ty = match dst_ty {
3118            InterfaceType::Tuple(t) => &self.types[*t],
3119            _ => panic!("expected a tuple"),
3120        };
3121
3122        // TODO: subtyping
3123        assert_eq!(src_ty.types.len(), dst_ty.types.len());
3124
3125        let srcs = src
3126            .record_field_srcs(self.types, src_ty.types.iter().copied())
3127            .zip(src_ty.types.iter());
3128        let dsts = dst
3129            .record_field_dsts(self.types, dst_ty.types.iter().copied())
3130            .zip(dst_ty.types.iter());
3131        for ((src, src_ty), (dst, dst_ty)) in srcs.zip(dsts) {
3132            self.translate(src_ty, &src, dst_ty, &dst);
3133        }
3134    }
3135
3136    fn translate_fixed_length_list(
3137        &mut self,
3138        src_ty: TypeFixedLengthListIndex,
3139        src: &Source<'_>,
3140        dst_ty: &InterfaceType,
3141        dst: &Destination,
3142    ) {
3143        let src_ty = &self.types[src_ty];
3144        let dst_ty = match dst_ty {
3145            InterfaceType::FixedLengthList(t) => &self.types[*t],
3146            _ => panic!("expected a fixed size list"),
3147        };
3148
3149        // TODO: subtyping
3150        assert_eq!(src_ty.size, dst_ty.size);
3151
3152        match (&src, &dst) {
3153            // Generate custom code for memory to memory copy
3154            (Source::Memory(src_mem), Destination::Memory(dst_mem)) => {
3155                let src_mem_opts = match &src_mem.opts.data_model {
3156                    DataModel::Gc {} => todo!("CM+GC"),
3157                    DataModel::LinearMemory(opts) => opts,
3158                };
3159                let dst_mem_opts = match &dst_mem.opts.data_model {
3160                    DataModel::Gc {} => todo!("CM+GC"),
3161                    DataModel::LinearMemory(opts) => opts,
3162                };
3163                let src_element_bytes = self.types.size_align(src_mem_opts, &src_ty.element).0;
3164                let dst_element_bytes = self.types.size_align(dst_mem_opts, &dst_ty.element).0;
3165                assert_ne!(src_element_bytes, 0);
3166                assert_ne!(dst_element_bytes, 0);
3167
3168                // because data is stored in-line, we assume that source and destination memory have been validated upstream
3169
3170                self.instruction(LocalGet(src_mem.addr.idx));
3171                if src_mem.offset != 0 {
3172                    self.ptr_uconst(src_mem_opts, src_mem.offset);
3173                    self.ptr_add(src_mem_opts);
3174                }
3175                let cur_src_ptr = self.local_set_new_tmp(src_mem_opts.ptr());
3176                self.instruction(LocalGet(dst_mem.addr.idx));
3177                if dst_mem.offset != 0 {
3178                    self.ptr_uconst(dst_mem_opts, dst_mem.offset);
3179                    self.ptr_add(dst_mem_opts);
3180                }
3181                let cur_dst_ptr = self.local_set_new_tmp(dst_mem_opts.ptr());
3182
3183                self.instruction(I32Const(src_ty.size as i32));
3184                let remaining = self.local_set_new_tmp(ValType::I32);
3185
3186                self.instruction(Loop(BlockType::Empty));
3187
3188                // Translate the next element in the list
3189                let element_src = Source::Memory(Memory {
3190                    opts: src_mem.opts,
3191                    offset: 0,
3192                    addr: TempLocal::new(cur_src_ptr.idx, cur_src_ptr.ty),
3193                });
3194                let element_dst = Destination::Memory(Memory {
3195                    opts: dst_mem.opts,
3196                    offset: 0,
3197                    addr: TempLocal::new(cur_dst_ptr.idx, cur_dst_ptr.ty),
3198                });
3199                self.translate(&src_ty.element, &element_src, &dst_ty.element, &element_dst);
3200
3201                // Update the two loop pointers
3202                self.instruction(LocalGet(cur_src_ptr.idx));
3203                self.ptr_uconst(src_mem_opts, src_element_bytes);
3204                self.ptr_add(src_mem_opts);
3205                self.instruction(LocalSet(cur_src_ptr.idx));
3206                self.instruction(LocalGet(cur_dst_ptr.idx));
3207                self.ptr_uconst(dst_mem_opts, dst_element_bytes);
3208                self.ptr_add(dst_mem_opts);
3209                self.instruction(LocalSet(cur_dst_ptr.idx));
3210
3211                // Update the remaining count, falling through to break out if it's zero
3212                // now.
3213                self.instruction(LocalGet(remaining.idx));
3214                self.ptr_iconst(src_mem_opts, -1);
3215                self.ptr_add(src_mem_opts);
3216                self.instruction(LocalTee(remaining.idx));
3217                self.ptr_br_if(src_mem_opts, 0);
3218                self.instruction(End); // end of loop
3219
3220                self.free_temp_local(cur_dst_ptr);
3221                self.free_temp_local(cur_src_ptr);
3222                self.free_temp_local(remaining);
3223                return;
3224            }
3225            // for the non-memory-to-memory case fall back to using generic tuple translation
3226            (_, _) => {
3227                // Assumes that the number of elements are small enough for this unrolling
3228                assert!(
3229                    src_ty.size as usize <= MAX_FLAT_PARAMS
3230                        && dst_ty.size as usize <= MAX_FLAT_PARAMS
3231                );
3232                let srcs =
3233                    src.record_field_srcs(self.types, (0..src_ty.size).map(|_| src_ty.element));
3234                let dsts =
3235                    dst.record_field_dsts(self.types, (0..dst_ty.size).map(|_| dst_ty.element));
3236                for (src, dst) in srcs.zip(dsts) {
3237                    self.translate(&src_ty.element, &src, &dst_ty.element, &dst);
3238                }
3239            }
3240        }
3241    }
3242
3243    fn translate_variant(
3244        &mut self,
3245        src_ty: TypeVariantIndex,
3246        src: &Source<'_>,
3247        dst_ty: &InterfaceType,
3248        dst: &Destination,
3249    ) {
3250        let src_ty = &self.types[src_ty];
3251        let dst_ty = match dst_ty {
3252            InterfaceType::Variant(t) => &self.types[*t],
3253            _ => panic!("expected a variant"),
3254        };
3255
3256        let src_info = variant_info(self.types, src_ty.cases.iter().map(|(_, c)| c.as_ref()));
3257        let dst_info = variant_info(self.types, dst_ty.cases.iter().map(|(_, c)| c.as_ref()));
3258
3259        let iter = src_ty
3260            .cases
3261            .iter()
3262            .enumerate()
3263            .map(|(src_i, (src_case, src_case_ty))| {
3264                let dst_i = dst_ty
3265                    .cases
3266                    .iter()
3267                    .position(|(c, _)| c == src_case)
3268                    .unwrap();
3269                let dst_case_ty = &dst_ty.cases[dst_i];
3270                let src_i = u32::try_from(src_i).unwrap();
3271                let dst_i = u32::try_from(dst_i).unwrap();
3272                VariantCase {
3273                    src_i,
3274                    src_ty: src_case_ty.as_ref(),
3275                    dst_i,
3276                    dst_ty: dst_case_ty.as_ref(),
3277                }
3278            });
3279        self.convert_variant(src, &src_info, dst, &dst_info, iter);
3280    }
3281
3282    fn translate_enum(
3283        &mut self,
3284        src_ty: TypeEnumIndex,
3285        src: &Source<'_>,
3286        dst_ty: &InterfaceType,
3287        dst: &Destination,
3288    ) {
3289        let src_ty = &self.types[src_ty];
3290        let dst_ty = match dst_ty {
3291            InterfaceType::Enum(t) => &self.types[*t],
3292            _ => panic!("expected an option"),
3293        };
3294
3295        debug_assert_eq!(src_ty.info.size, dst_ty.info.size);
3296        debug_assert_eq!(src_ty.names.len(), dst_ty.names.len());
3297        debug_assert!(
3298            src_ty
3299                .names
3300                .iter()
3301                .zip(dst_ty.names.iter())
3302                .all(|(a, b)| a == b)
3303        );
3304
3305        // Get the discriminant.
3306        match src {
3307            Source::Stack(s) => self.stack_get(&s.slice(0..1), ValType::I32),
3308            Source::Memory(mem) => match src_ty.info.size {
3309                DiscriminantSize::Size1 => self.i32_load8u(mem),
3310                DiscriminantSize::Size2 => self.i32_load16u(mem),
3311                DiscriminantSize::Size4 => self.i32_load(mem),
3312            },
3313            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
3314        }
3315        let tmp = self.local_tee_new_tmp(ValType::I32);
3316
3317        // Assert that the discriminant is valid.
3318        self.instruction(I32Const(i32::try_from(src_ty.names.len()).unwrap()));
3319        self.instruction(I32GeU);
3320        self.instruction(If(BlockType::Empty));
3321        self.trap(Trap::InvalidDiscriminant);
3322        self.instruction(End);
3323
3324        // Save the discriminant to the destination.
3325        match dst {
3326            Destination::Stack(stack, _) => {
3327                self.local_get_tmp(&tmp);
3328                self.stack_set(&stack[..1], ValType::I32)
3329            }
3330            Destination::Memory(mem) => {
3331                self.push_dst_addr(dst);
3332                self.local_get_tmp(&tmp);
3333                match dst_ty.info.size {
3334                    DiscriminantSize::Size1 => self.i32_store8(mem),
3335                    DiscriminantSize::Size2 => self.i32_store16(mem),
3336                    DiscriminantSize::Size4 => self.i32_store(mem),
3337                }
3338            }
3339            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
3340        }
3341        self.free_temp_local(tmp);
3342    }
3343
3344    fn translate_option(
3345        &mut self,
3346        src_ty: TypeOptionIndex,
3347        src: &Source<'_>,
3348        dst_ty: &InterfaceType,
3349        dst: &Destination,
3350    ) {
3351        let src_ty = &self.types[src_ty].ty;
3352        let dst_ty = match dst_ty {
3353            InterfaceType::Option(t) => &self.types[*t].ty,
3354            _ => panic!("expected an option"),
3355        };
3356        let src_ty = Some(src_ty);
3357        let dst_ty = Some(dst_ty);
3358
3359        let src_info = variant_info(self.types, [None, src_ty]);
3360        let dst_info = variant_info(self.types, [None, dst_ty]);
3361
3362        self.convert_variant(
3363            src,
3364            &src_info,
3365            dst,
3366            &dst_info,
3367            [
3368                VariantCase {
3369                    src_i: 0,
3370                    dst_i: 0,
3371                    src_ty: None,
3372                    dst_ty: None,
3373                },
3374                VariantCase {
3375                    src_i: 1,
3376                    dst_i: 1,
3377                    src_ty,
3378                    dst_ty,
3379                },
3380            ]
3381            .into_iter(),
3382        );
3383    }
3384
3385    fn translate_result(
3386        &mut self,
3387        src_ty: TypeResultIndex,
3388        src: &Source<'_>,
3389        dst_ty: &InterfaceType,
3390        dst: &Destination,
3391    ) {
3392        let src_ty = &self.types[src_ty];
3393        let dst_ty = match dst_ty {
3394            InterfaceType::Result(t) => &self.types[*t],
3395            _ => panic!("expected a result"),
3396        };
3397
3398        let src_info = variant_info(self.types, [src_ty.ok.as_ref(), src_ty.err.as_ref()]);
3399        let dst_info = variant_info(self.types, [dst_ty.ok.as_ref(), dst_ty.err.as_ref()]);
3400
3401        self.convert_variant(
3402            src,
3403            &src_info,
3404            dst,
3405            &dst_info,
3406            [
3407                VariantCase {
3408                    src_i: 0,
3409                    dst_i: 0,
3410                    src_ty: src_ty.ok.as_ref(),
3411                    dst_ty: dst_ty.ok.as_ref(),
3412                },
3413                VariantCase {
3414                    src_i: 1,
3415                    dst_i: 1,
3416                    src_ty: src_ty.err.as_ref(),
3417                    dst_ty: dst_ty.err.as_ref(),
3418                },
3419            ]
3420            .into_iter(),
3421        );
3422    }
3423
3424    fn convert_variant<'c>(
3425        &mut self,
3426        src: &Source<'_>,
3427        src_info: &VariantInfo,
3428        dst: &Destination,
3429        dst_info: &VariantInfo,
3430        src_cases: impl ExactSizeIterator<Item = VariantCase<'c>>,
3431    ) {
3432        // The outermost block is special since it has the result type of the
3433        // translation here. That will depend on the `dst`.
3434        let outer_block_ty = match dst {
3435            Destination::Stack(dst_flat, _) => match dst_flat.len() {
3436                0 => BlockType::Empty,
3437                1 => BlockType::Result(dst_flat[0]),
3438                _ => {
3439                    let ty = self.module.core_types.function(&[], &dst_flat);
3440                    BlockType::FunctionType(ty)
3441                }
3442            },
3443            Destination::Memory(_) => BlockType::Empty,
3444            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
3445        };
3446        self.instruction(Block(outer_block_ty));
3447
3448        // After the outermost block generate a new block for each of the
3449        // remaining cases.
3450        let src_cases_len = src_cases.len();
3451        for _ in 0..src_cases_len - 1 {
3452            self.instruction(Block(BlockType::Empty));
3453        }
3454
3455        // Generate a block for an invalid variant discriminant
3456        self.instruction(Block(BlockType::Empty));
3457
3458        // And generate one final block that we'll be jumping out of with the
3459        // `br_table`
3460        self.instruction(Block(BlockType::Empty));
3461
3462        // Load the discriminant
3463        match src {
3464            Source::Stack(s) => self.stack_get(&s.slice(0..1), ValType::I32),
3465            Source::Memory(mem) => match src_info.size {
3466                DiscriminantSize::Size1 => self.i32_load8u(mem),
3467                DiscriminantSize::Size2 => self.i32_load16u(mem),
3468                DiscriminantSize::Size4 => self.i32_load(mem),
3469            },
3470            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
3471        }
3472
3473        // Generate the `br_table` for the discriminant. Each case has an
3474        // offset of 1 to skip the trapping block.
3475        let mut targets = Vec::new();
3476        for i in 0..src_cases_len {
3477            targets.push((i + 1) as u32);
3478        }
3479        self.instruction(BrTable(targets[..].into(), 0));
3480        self.instruction(End); // end the `br_table` block
3481
3482        self.trap(Trap::InvalidDiscriminant);
3483        self.instruction(End); // end the "invalid discriminant" block
3484
3485        // Translate each case individually within its own block. Note that the
3486        // iteration order here places the first case in the innermost block
3487        // and the last case in the outermost block. This matches the order
3488        // of the jump targets in the `br_table` instruction.
3489        let src_cases_len = u32::try_from(src_cases_len).unwrap();
3490        for case in src_cases {
3491            let VariantCase {
3492                src_i,
3493                src_ty,
3494                dst_i,
3495                dst_ty,
3496            } = case;
3497
3498            // Translate the discriminant here, noting that `dst_i` may be
3499            // different than `src_i`.
3500            self.push_dst_addr(dst);
3501            self.instruction(I32Const(dst_i as i32));
3502            match dst {
3503                Destination::Stack(stack, _) => self.stack_set(&stack[..1], ValType::I32),
3504                Destination::Memory(mem) => match dst_info.size {
3505                    DiscriminantSize::Size1 => self.i32_store8(mem),
3506                    DiscriminantSize::Size2 => self.i32_store16(mem),
3507                    DiscriminantSize::Size4 => self.i32_store(mem),
3508                },
3509                Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
3510            }
3511
3512            let src_payload = src.payload_src(self.types, src_info, src_ty);
3513            let dst_payload = dst.payload_dst(self.types, dst_info, dst_ty);
3514
3515            // Translate the payload of this case using the various types from
3516            // the dst/src.
3517            match (src_ty, dst_ty) {
3518                (Some(src_ty), Some(dst_ty)) => {
3519                    self.translate(src_ty, &src_payload, dst_ty, &dst_payload);
3520                }
3521                (None, None) => {}
3522                _ => unimplemented!(),
3523            }
3524
3525            // If the results of this translation were placed on the stack then
3526            // the stack values may need to be padded with more zeros due to
3527            // this particular case being possibly smaller than the entire
3528            // variant. That's handled here by pushing remaining zeros after
3529            // accounting for the discriminant pushed as well as the results of
3530            // this individual payload.
3531            if let Destination::Stack(payload_results, _) = dst_payload {
3532                if let Destination::Stack(dst_results, _) = dst {
3533                    let remaining = &dst_results[1..][payload_results.len()..];
3534                    for ty in remaining {
3535                        match ty {
3536                            ValType::I32 => self.instruction(I32Const(0)),
3537                            ValType::I64 => self.instruction(I64Const(0)),
3538                            ValType::F32 => self.instruction(F32Const(0.0.into())),
3539                            ValType::F64 => self.instruction(F64Const(0.0.into())),
3540                            _ => unreachable!(),
3541                        }
3542                    }
3543                }
3544            }
3545
3546            // Branch to the outermost block. Note that this isn't needed for
3547            // the outermost case since it simply falls through.
3548            if src_i != src_cases_len - 1 {
3549                self.instruction(Br(src_cases_len - src_i - 1));
3550            }
3551            self.instruction(End); // end this case's block
3552        }
3553    }
3554
3555    fn translate_future(
3556        &mut self,
3557        src_ty: TypeFutureTableIndex,
3558        src: &Source<'_>,
3559        dst_ty: &InterfaceType,
3560        dst: &Destination,
3561    ) {
3562        let dst_ty = match dst_ty {
3563            InterfaceType::Future(t) => *t,
3564            _ => panic!("expected a `Future`"),
3565        };
3566        let transfer = self.module.import_future_transfer();
3567        self.translate_handle(src_ty.as_u32(), src, dst_ty.as_u32(), dst, transfer);
3568    }
3569
3570    fn translate_stream(
3571        &mut self,
3572        src_ty: TypeStreamTableIndex,
3573        src: &Source<'_>,
3574        dst_ty: &InterfaceType,
3575        dst: &Destination,
3576    ) {
3577        let dst_ty = match dst_ty {
3578            InterfaceType::Stream(t) => *t,
3579            _ => panic!("expected a `Stream`"),
3580        };
3581        let transfer = self.module.import_stream_transfer();
3582        self.translate_handle(src_ty.as_u32(), src, dst_ty.as_u32(), dst, transfer);
3583    }
3584
3585    fn translate_error_context(
3586        &mut self,
3587        src_ty: TypeComponentLocalErrorContextTableIndex,
3588        src: &Source<'_>,
3589        dst_ty: &InterfaceType,
3590        dst: &Destination,
3591    ) {
3592        let dst_ty = match dst_ty {
3593            InterfaceType::ErrorContext(t) => *t,
3594            _ => panic!("expected an `ErrorContext`"),
3595        };
3596        let transfer = self.module.import_error_context_transfer();
3597        self.translate_handle(src_ty.as_u32(), src, dst_ty.as_u32(), dst, transfer);
3598    }
3599
3600    fn translate_own(
3601        &mut self,
3602        src_ty: TypeResourceTableIndex,
3603        src: &Source<'_>,
3604        dst_ty: &InterfaceType,
3605        dst: &Destination,
3606    ) {
3607        let dst_ty = match dst_ty {
3608            InterfaceType::Own(t) => *t,
3609            _ => panic!("expected an `Own`"),
3610        };
3611        let transfer = self.module.import_resource_transfer_own();
3612        self.translate_handle(src_ty.as_u32(), src, dst_ty.as_u32(), dst, transfer);
3613    }
3614
3615    fn translate_borrow(
3616        &mut self,
3617        src_ty: TypeResourceTableIndex,
3618        src: &Source<'_>,
3619        dst_ty: &InterfaceType,
3620        dst: &Destination,
3621    ) {
3622        let dst_ty = match dst_ty {
3623            InterfaceType::Borrow(t) => *t,
3624            _ => panic!("expected an `Borrow`"),
3625        };
3626
3627        let transfer = self.module.import_resource_transfer_borrow();
3628        self.translate_handle(src_ty.as_u32(), src, dst_ty.as_u32(), dst, transfer);
3629    }
3630
3631    /// Translates the index `src`, which resides in the table `src_ty`, into
3632    /// and index within `dst_ty` and is stored at `dst`.
3633    ///
3634    /// Actual translation of the index happens in a wasmtime libcall, which a
3635    /// cranelift-generated trampoline to satisfy this import will call. The
3636    /// `transfer` function is an imported function which takes the src, src_ty,
3637    /// and dst_ty, and returns the dst index.
3638    fn translate_handle(
3639        &mut self,
3640        src_ty: u32,
3641        src: &Source<'_>,
3642        dst_ty: u32,
3643        dst: &Destination,
3644        transfer: FuncIndex,
3645    ) {
3646        self.push_dst_addr(dst);
3647        match src {
3648            Source::Memory(mem) => self.i32_load(mem),
3649            Source::Stack(stack) => self.stack_get(stack, ValType::I32),
3650            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
3651        }
3652        self.instruction(I32Const(src_ty as i32));
3653        self.instruction(I32Const(dst_ty as i32));
3654        self.instruction(Call(transfer.as_u32()));
3655        match dst {
3656            Destination::Memory(mem) => self.i32_store(mem),
3657            Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
3658            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
3659        }
3660    }
3661
3662    fn trap_if_not_flag(&mut self, flags_global: GlobalIndex, flag_to_test: i32, trap: Trap) {
3663        self.instruction(GlobalGet(flags_global.as_u32()));
3664        self.instruction(I32Const(flag_to_test));
3665        self.instruction(I32And);
3666        self.instruction(I32Eqz);
3667        self.instruction(If(BlockType::Empty));
3668        self.trap(trap);
3669        self.instruction(End);
3670    }
3671
3672    fn set_flag(&mut self, flags_global: GlobalIndex, flag_to_set: i32, value: bool) {
3673        self.instruction(GlobalGet(flags_global.as_u32()));
3674        if value {
3675            self.instruction(I32Const(flag_to_set));
3676            self.instruction(I32Or);
3677        } else {
3678            self.instruction(I32Const(!flag_to_set));
3679            self.instruction(I32And);
3680        }
3681        self.instruction(GlobalSet(flags_global.as_u32()));
3682    }
3683
3684    fn verify_aligned(&mut self, opts: &LinearMemoryOptions, addr_local: u32, align: u32) {
3685        // If the alignment is 1 then everything is trivially aligned and the
3686        // check can be omitted.
3687        if align == 1 {
3688            return;
3689        }
3690        self.instruction(LocalGet(addr_local));
3691        assert!(align.is_power_of_two());
3692        self.ptr_uconst(opts, align - 1);
3693        self.ptr_and(opts);
3694        self.ptr_if(opts, BlockType::Empty);
3695        self.trap(Trap::UnalignedPointer);
3696        self.instruction(End);
3697    }
3698
3699    fn assert_aligned(&mut self, ty: &InterfaceType, mem: &Memory) {
3700        let mem_opts = mem.mem_opts();
3701        if !self.module.tunables.debug_adapter_modules {
3702            return;
3703        }
3704        let align = self.types.align(mem_opts, ty);
3705        if align == 1 {
3706            return;
3707        }
3708        assert!(align.is_power_of_two());
3709        self.instruction(LocalGet(mem.addr.idx));
3710        self.ptr_uconst(mem_opts, mem.offset);
3711        self.ptr_add(mem_opts);
3712        self.ptr_uconst(mem_opts, align - 1);
3713        self.ptr_and(mem_opts);
3714        self.ptr_if(mem_opts, BlockType::Empty);
3715        self.trap(Trap::DebugAssertPointerAligned);
3716        self.instruction(End);
3717    }
3718
3719    fn malloc<'c>(&mut self, opts: &'c Options, size: MallocSize, align: u32) -> Memory<'c> {
3720        match &opts.data_model {
3721            DataModel::Gc {} => todo!("CM+GC"),
3722            DataModel::LinearMemory(mem_opts) => {
3723                let realloc = mem_opts.realloc.unwrap();
3724                self.ptr_uconst(mem_opts, 0);
3725                self.ptr_uconst(mem_opts, 0);
3726                self.ptr_uconst(mem_opts, align);
3727                match size {
3728                    MallocSize::Const(size) => self.ptr_uconst(mem_opts, size),
3729                    MallocSize::Local(idx) => self.instruction(LocalGet(idx)),
3730                }
3731                self.instruction(Call(realloc.as_u32()));
3732                let addr = self.local_set_new_tmp(mem_opts.ptr());
3733                self.memory_operand(opts, addr, align)
3734            }
3735        }
3736    }
3737
3738    fn memory_operand<'c>(&mut self, opts: &'c Options, addr: TempLocal, align: u32) -> Memory<'c> {
3739        let ret = Memory {
3740            addr,
3741            offset: 0,
3742            opts,
3743        };
3744        self.verify_aligned(opts.data_model.unwrap_memory(), ret.addr.idx, align);
3745        ret
3746    }
3747
3748    /// Generates a new local in this function of the `ty` specified,
3749    /// initializing it with the top value on the current wasm stack.
3750    ///
3751    /// The returned `TempLocal` must be freed after it is finished with
3752    /// `free_temp_local`.
3753    fn local_tee_new_tmp(&mut self, ty: ValType) -> TempLocal {
3754        self.gen_temp_local(ty, LocalTee)
3755    }
3756
3757    /// Same as `local_tee_new_tmp` but initializes the local with `LocalSet`
3758    /// instead of `LocalTee`.
3759    fn local_set_new_tmp(&mut self, ty: ValType) -> TempLocal {
3760        self.gen_temp_local(ty, LocalSet)
3761    }
3762
3763    fn local_get_tmp(&mut self, local: &TempLocal) {
3764        self.instruction(LocalGet(local.idx));
3765    }
3766
3767    fn gen_temp_local(&mut self, ty: ValType, insn: fn(u32) -> Instruction<'static>) -> TempLocal {
3768        // First check to see if any locals are available in this function which
3769        // were previously generated but are no longer in use.
3770        if let Some(idx) = self.free_locals.get_mut(&ty).and_then(|v| v.pop()) {
3771            self.instruction(insn(idx));
3772            return TempLocal {
3773                ty,
3774                idx,
3775                needs_free: true,
3776            };
3777        }
3778
3779        // Failing that generate a fresh new local.
3780        let locals = &mut self.module.funcs[self.result].locals;
3781        match locals.last_mut() {
3782            Some((cnt, prev_ty)) if ty == *prev_ty => *cnt += 1,
3783            _ => locals.push((1, ty)),
3784        }
3785        self.nlocals += 1;
3786        let idx = self.nlocals - 1;
3787        self.instruction(insn(idx));
3788        TempLocal {
3789            ty,
3790            idx,
3791            needs_free: true,
3792        }
3793    }
3794
3795    /// Used to release a `TempLocal` from a particular lexical scope to allow
3796    /// its possible reuse in later scopes.
3797    fn free_temp_local(&mut self, mut local: TempLocal) {
3798        assert!(local.needs_free);
3799        self.free_locals
3800            .entry(local.ty)
3801            .or_insert(Vec::new())
3802            .push(local.idx);
3803        local.needs_free = false;
3804    }
3805
3806    fn instruction(&mut self, instr: Instruction) {
3807        instr.encode(&mut self.code);
3808    }
3809
3810    fn trap(&mut self, trap: Trap) {
3811        let trap_func = self.module.import_trap();
3812        self.instruction(I32Const(trap as i32));
3813        self.instruction(Call(trap_func.as_u32()));
3814        self.instruction(Unreachable);
3815    }
3816
3817    /// Flushes out the current `code` instructions into the destination
3818    /// function.
3819    ///
3820    /// This is a noop if no instructions have been encoded yet.
3821    fn flush_code(&mut self) {
3822        if self.code.is_empty() {
3823            return;
3824        }
3825        self.module.funcs[self.result]
3826            .body
3827            .push(Body::Raw(mem::take(&mut self.code)));
3828    }
3829
3830    fn finish(mut self) {
3831        // Append the final `end` instruction which all functions require, and
3832        // then empty out the temporary buffer in `Compiler`.
3833        self.instruction(End);
3834        self.flush_code();
3835
3836        // Flag the function as "done" which helps with an assert later on in
3837        // emission that everything was eventually finished.
3838        self.module.funcs[self.result].filled_in = true;
3839    }
3840
3841    /// Fetches the value contained with the local specified by `stack` and
3842    /// converts it to `dst_ty`.
3843    ///
3844    /// This is only intended for use in primitive operations where `stack` is
3845    /// guaranteed to have only one local. The type of the local on the stack is
3846    /// then converted to `dst_ty` appropriately. Note that the types may be
3847    /// different due to the "flattening" of variant types.
3848    fn stack_get(&mut self, stack: &Stack<'_>, dst_ty: ValType) {
3849        assert_eq!(stack.locals.len(), 1);
3850        let (idx, src_ty) = stack.locals[0];
3851        self.instruction(LocalGet(idx));
3852        match (src_ty, dst_ty) {
3853            (ValType::I32, ValType::I32)
3854            | (ValType::I64, ValType::I64)
3855            | (ValType::F32, ValType::F32)
3856            | (ValType::F64, ValType::F64) => {}
3857
3858            (ValType::I32, ValType::F32) => self.instruction(F32ReinterpretI32),
3859            (ValType::I64, ValType::I32) => {
3860                self.assert_i64_upper_bits_not_set(idx);
3861                self.instruction(I32WrapI64);
3862            }
3863            (ValType::I64, ValType::F64) => self.instruction(F64ReinterpretI64),
3864            (ValType::I64, ValType::F32) => {
3865                self.assert_i64_upper_bits_not_set(idx);
3866                self.instruction(I32WrapI64);
3867                self.instruction(F32ReinterpretI32);
3868            }
3869
3870            // should not be possible given the `join` function for variants
3871            (ValType::I32, ValType::I64)
3872            | (ValType::I32, ValType::F64)
3873            | (ValType::F32, ValType::I32)
3874            | (ValType::F32, ValType::I64)
3875            | (ValType::F32, ValType::F64)
3876            | (ValType::F64, ValType::I32)
3877            | (ValType::F64, ValType::I64)
3878            | (ValType::F64, ValType::F32)
3879
3880            // not used in the component model
3881            | (ValType::Ref(_), _)
3882            | (_, ValType::Ref(_))
3883            | (ValType::V128, _)
3884            | (_, ValType::V128) => {
3885                panic!("cannot get {dst_ty:?} from {src_ty:?} local");
3886            }
3887        }
3888    }
3889
3890    fn assert_i64_upper_bits_not_set(&mut self, local: u32) {
3891        if !self.module.tunables.debug_adapter_modules {
3892            return;
3893        }
3894        self.instruction(LocalGet(local));
3895        self.instruction(I64Const(32));
3896        self.instruction(I64ShrU);
3897        self.instruction(I32WrapI64);
3898        self.instruction(If(BlockType::Empty));
3899        self.trap(Trap::DebugAssertUpperBitsUnset);
3900        self.instruction(End);
3901    }
3902
3903    /// Converts the top value on the WebAssembly stack which has type
3904    /// `src_ty` to `dst_tys[0]`.
3905    ///
3906    /// This is only intended for conversion of primitives where the `dst_tys`
3907    /// list is known to be of length 1.
3908    fn stack_set(&mut self, dst_tys: &[ValType], src_ty: ValType) {
3909        assert_eq!(dst_tys.len(), 1);
3910        let dst_ty = dst_tys[0];
3911        match (src_ty, dst_ty) {
3912            (ValType::I32, ValType::I32)
3913            | (ValType::I64, ValType::I64)
3914            | (ValType::F32, ValType::F32)
3915            | (ValType::F64, ValType::F64) => {}
3916
3917            (ValType::F32, ValType::I32) => self.instruction(I32ReinterpretF32),
3918            (ValType::I32, ValType::I64) => self.instruction(I64ExtendI32U),
3919            (ValType::F64, ValType::I64) => self.instruction(I64ReinterpretF64),
3920            (ValType::F32, ValType::I64) => {
3921                self.instruction(I32ReinterpretF32);
3922                self.instruction(I64ExtendI32U);
3923            }
3924
3925            // should not be possible given the `join` function for variants
3926            (ValType::I64, ValType::I32)
3927            | (ValType::F64, ValType::I32)
3928            | (ValType::I32, ValType::F32)
3929            | (ValType::I64, ValType::F32)
3930            | (ValType::F64, ValType::F32)
3931            | (ValType::I32, ValType::F64)
3932            | (ValType::I64, ValType::F64)
3933            | (ValType::F32, ValType::F64)
3934
3935            // not used in the component model
3936            | (ValType::Ref(_), _)
3937            | (_, ValType::Ref(_))
3938            | (ValType::V128, _)
3939            | (_, ValType::V128) => {
3940                panic!("cannot get {dst_ty:?} from {src_ty:?} local");
3941            }
3942        }
3943    }
3944
3945    fn i32_load8u(&mut self, mem: &Memory) {
3946        self.instruction(LocalGet(mem.addr.idx));
3947        self.instruction(I32Load8U(mem.memarg(0)));
3948    }
3949
3950    fn i32_load8s(&mut self, mem: &Memory) {
3951        self.instruction(LocalGet(mem.addr.idx));
3952        self.instruction(I32Load8S(mem.memarg(0)));
3953    }
3954
3955    fn i32_load16u(&mut self, mem: &Memory) {
3956        self.instruction(LocalGet(mem.addr.idx));
3957        self.instruction(I32Load16U(mem.memarg(1)));
3958    }
3959
3960    fn i32_load16s(&mut self, mem: &Memory) {
3961        self.instruction(LocalGet(mem.addr.idx));
3962        self.instruction(I32Load16S(mem.memarg(1)));
3963    }
3964
3965    fn i32_load(&mut self, mem: &Memory) {
3966        self.instruction(LocalGet(mem.addr.idx));
3967        self.instruction(I32Load(mem.memarg(2)));
3968    }
3969
3970    fn i64_load(&mut self, mem: &Memory) {
3971        self.instruction(LocalGet(mem.addr.idx));
3972        self.instruction(I64Load(mem.memarg(3)));
3973    }
3974
3975    fn ptr_load(&mut self, mem: &Memory) {
3976        if mem.mem_opts().memory64 {
3977            self.i64_load(mem);
3978        } else {
3979            self.i32_load(mem);
3980        }
3981    }
3982
3983    fn ptr_add(&mut self, opts: &LinearMemoryOptions) {
3984        if opts.memory64 {
3985            self.instruction(I64Add);
3986        } else {
3987            self.instruction(I32Add);
3988        }
3989    }
3990
3991    fn ptr_sub(&mut self, opts: &LinearMemoryOptions) {
3992        if opts.memory64 {
3993            self.instruction(I64Sub);
3994        } else {
3995            self.instruction(I32Sub);
3996        }
3997    }
3998
3999    fn ptr_mul(&mut self, opts: &LinearMemoryOptions) {
4000        if opts.memory64 {
4001            self.instruction(I64Mul);
4002        } else {
4003            self.instruction(I32Mul);
4004        }
4005    }
4006
4007    fn ptr_ge_u(&mut self, opts: &LinearMemoryOptions) {
4008        if opts.memory64 {
4009            self.instruction(I64GeU);
4010        } else {
4011            self.instruction(I32GeU);
4012        }
4013    }
4014
4015    fn ptr_lt_u(&mut self, opts: &LinearMemoryOptions) {
4016        if opts.memory64 {
4017            self.instruction(I64LtU);
4018        } else {
4019            self.instruction(I32LtU);
4020        }
4021    }
4022
4023    fn ptr_shl(&mut self, opts: &LinearMemoryOptions) {
4024        if opts.memory64 {
4025            self.instruction(I64Shl);
4026        } else {
4027            self.instruction(I32Shl);
4028        }
4029    }
4030
4031    fn ptr_eqz(&mut self, opts: &LinearMemoryOptions) {
4032        if opts.memory64 {
4033            self.instruction(I64Eqz);
4034        } else {
4035            self.instruction(I32Eqz);
4036        }
4037    }
4038
4039    fn ptr_uconst(&mut self, opts: &LinearMemoryOptions, val: u32) {
4040        if opts.memory64 {
4041            self.instruction(I64Const(val.into()));
4042        } else {
4043            self.instruction(I32Const(val as i32));
4044        }
4045    }
4046
4047    fn ptr_iconst(&mut self, opts: &LinearMemoryOptions, val: i32) {
4048        if opts.memory64 {
4049            self.instruction(I64Const(val.into()));
4050        } else {
4051            self.instruction(I32Const(val));
4052        }
4053    }
4054
4055    fn ptr_eq(&mut self, opts: &LinearMemoryOptions) {
4056        if opts.memory64 {
4057            self.instruction(I64Eq);
4058        } else {
4059            self.instruction(I32Eq);
4060        }
4061    }
4062
4063    fn ptr_ne(&mut self, opts: &LinearMemoryOptions) {
4064        if opts.memory64 {
4065            self.instruction(I64Ne);
4066        } else {
4067            self.instruction(I32Ne);
4068        }
4069    }
4070
4071    fn ptr_and(&mut self, opts: &LinearMemoryOptions) {
4072        if opts.memory64 {
4073            self.instruction(I64And);
4074        } else {
4075            self.instruction(I32And);
4076        }
4077    }
4078
4079    fn ptr_or(&mut self, opts: &LinearMemoryOptions) {
4080        if opts.memory64 {
4081            self.instruction(I64Or);
4082        } else {
4083            self.instruction(I32Or);
4084        }
4085    }
4086
4087    fn ptr_xor(&mut self, opts: &LinearMemoryOptions) {
4088        if opts.memory64 {
4089            self.instruction(I64Xor);
4090        } else {
4091            self.instruction(I32Xor);
4092        }
4093    }
4094
4095    fn ptr_if(&mut self, opts: &LinearMemoryOptions, ty: BlockType) {
4096        if opts.memory64 {
4097            self.instruction(I64Const(0));
4098            self.instruction(I64Ne);
4099        }
4100        self.instruction(If(ty));
4101    }
4102
4103    fn ptr_br_if(&mut self, opts: &LinearMemoryOptions, depth: u32) {
4104        if opts.memory64 {
4105            self.instruction(I64Const(0));
4106            self.instruction(I64Ne);
4107        }
4108        self.instruction(BrIf(depth));
4109    }
4110
4111    fn f32_load(&mut self, mem: &Memory) {
4112        self.instruction(LocalGet(mem.addr.idx));
4113        self.instruction(F32Load(mem.memarg(2)));
4114    }
4115
4116    fn f64_load(&mut self, mem: &Memory) {
4117        self.instruction(LocalGet(mem.addr.idx));
4118        self.instruction(F64Load(mem.memarg(3)));
4119    }
4120
4121    fn push_dst_addr(&mut self, dst: &Destination) {
4122        if let Destination::Memory(mem) = dst {
4123            self.instruction(LocalGet(mem.addr.idx));
4124        }
4125    }
4126
4127    fn i32_store8(&mut self, mem: &Memory) {
4128        self.instruction(I32Store8(mem.memarg(0)));
4129    }
4130
4131    fn i32_store16(&mut self, mem: &Memory) {
4132        self.instruction(I32Store16(mem.memarg(1)));
4133    }
4134
4135    fn i32_store(&mut self, mem: &Memory) {
4136        self.instruction(I32Store(mem.memarg(2)));
4137    }
4138
4139    fn i64_store(&mut self, mem: &Memory) {
4140        self.instruction(I64Store(mem.memarg(3)));
4141    }
4142
4143    fn ptr_store(&mut self, mem: &Memory) {
4144        if mem.mem_opts().memory64 {
4145            self.i64_store(mem);
4146        } else {
4147            self.i32_store(mem);
4148        }
4149    }
4150
4151    fn f32_store(&mut self, mem: &Memory) {
4152        self.instruction(F32Store(mem.memarg(2)));
4153    }
4154
4155    fn f64_store(&mut self, mem: &Memory) {
4156        self.instruction(F64Store(mem.memarg(3)));
4157    }
4158}
4159
4160impl<'a> Source<'a> {
4161    /// Given this `Source` returns an iterator over the `Source` for each of
4162    /// the component `fields` specified.
4163    ///
4164    /// This will automatically slice stack-based locals to the appropriate
4165    /// width for each component type and additionally calculate the appropriate
4166    /// offset for each memory-based type.
4167    fn record_field_srcs<'b>(
4168        &'b self,
4169        types: &'b ComponentTypesBuilder,
4170        fields: impl IntoIterator<Item = InterfaceType> + 'b,
4171    ) -> impl Iterator<Item = Source<'a>> + 'b
4172    where
4173        'a: 'b,
4174    {
4175        let mut offset = 0;
4176        fields.into_iter().map(move |ty| match self {
4177            Source::Memory(mem) => {
4178                let mem = next_field_offset(&mut offset, types, &ty, mem);
4179                Source::Memory(mem)
4180            }
4181            Source::Stack(stack) => {
4182                let cnt = types.flat_types(&ty).unwrap().len() as u32;
4183                offset += cnt;
4184                Source::Stack(stack.slice((offset - cnt) as usize..offset as usize))
4185            }
4186            Source::Struct(_) => todo!(),
4187            Source::Array(_) => todo!(),
4188        })
4189    }
4190
4191    /// Returns the corresponding discriminant source and payload source f
4192    fn payload_src(
4193        &self,
4194        types: &ComponentTypesBuilder,
4195        info: &VariantInfo,
4196        case: Option<&InterfaceType>,
4197    ) -> Source<'a> {
4198        match self {
4199            Source::Stack(s) => {
4200                let flat_len = match case {
4201                    Some(case) => types.flat_types(case).unwrap().len(),
4202                    None => 0,
4203                };
4204                Source::Stack(s.slice(1..s.locals.len()).slice(0..flat_len))
4205            }
4206            Source::Memory(mem) => {
4207                let mem = if mem.mem_opts().memory64 {
4208                    mem.bump(info.payload_offset64)
4209                } else {
4210                    mem.bump(info.payload_offset32)
4211                };
4212                Source::Memory(mem)
4213            }
4214            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
4215        }
4216    }
4217
4218    fn opts(&self) -> &'a Options {
4219        match self {
4220            Source::Stack(s) => s.opts,
4221            Source::Memory(mem) => mem.opts,
4222            Source::Struct(s) => s.opts,
4223            Source::Array(a) => a.opts,
4224        }
4225    }
4226}
4227
4228impl<'a> Destination<'a> {
4229    /// Same as `Source::record_field_srcs` but for destinations.
4230    fn record_field_dsts<'b, I>(
4231        &'b self,
4232        types: &'b ComponentTypesBuilder,
4233        fields: I,
4234    ) -> impl Iterator<Item = Destination<'b>> + use<'b, I>
4235    where
4236        'a: 'b,
4237        I: IntoIterator<Item = InterfaceType> + 'b,
4238    {
4239        let mut offset = 0;
4240        fields.into_iter().map(move |ty| match self {
4241            Destination::Memory(mem) => {
4242                let mem = next_field_offset(&mut offset, types, &ty, mem);
4243                Destination::Memory(mem)
4244            }
4245            Destination::Stack(s, opts) => {
4246                let cnt = types.flat_types(&ty).unwrap().len() as u32;
4247                offset += cnt;
4248                Destination::Stack(&s[(offset - cnt) as usize..offset as usize], opts)
4249            }
4250            Destination::Struct(_) => todo!(),
4251            Destination::Array(_) => todo!(),
4252        })
4253    }
4254
4255    /// Returns the corresponding discriminant source and payload source f
4256    fn payload_dst(
4257        &self,
4258        types: &ComponentTypesBuilder,
4259        info: &VariantInfo,
4260        case: Option<&InterfaceType>,
4261    ) -> Destination<'_> {
4262        match self {
4263            Destination::Stack(s, opts) => {
4264                let flat_len = match case {
4265                    Some(case) => types.flat_types(case).unwrap().len(),
4266                    None => 0,
4267                };
4268                Destination::Stack(&s[1..][..flat_len], opts)
4269            }
4270            Destination::Memory(mem) => {
4271                let mem = if mem.mem_opts().memory64 {
4272                    mem.bump(info.payload_offset64)
4273                } else {
4274                    mem.bump(info.payload_offset32)
4275                };
4276                Destination::Memory(mem)
4277            }
4278            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
4279        }
4280    }
4281
4282    fn opts(&self) -> &'a Options {
4283        match self {
4284            Destination::Stack(_, opts) => opts,
4285            Destination::Memory(mem) => mem.opts,
4286            Destination::Struct(s) => s.opts,
4287            Destination::Array(a) => a.opts,
4288        }
4289    }
4290}
4291
4292fn next_field_offset<'a>(
4293    offset: &mut u32,
4294    types: &ComponentTypesBuilder,
4295    field: &InterfaceType,
4296    mem: &Memory<'a>,
4297) -> Memory<'a> {
4298    let abi = types.canonical_abi(field);
4299    let offset = if mem.mem_opts().memory64 {
4300        abi.next_field64(offset)
4301    } else {
4302        abi.next_field32(offset)
4303    };
4304    mem.bump(offset)
4305}
4306
4307impl<'a> Memory<'a> {
4308    fn memarg(&self, align: u32) -> MemArg {
4309        MemArg {
4310            offset: u64::from(self.offset),
4311            align,
4312            memory_index: self.mem_opts().memory.unwrap().as_u32(),
4313        }
4314    }
4315
4316    fn bump(&self, offset: u32) -> Memory<'a> {
4317        Memory {
4318            opts: self.opts,
4319            addr: TempLocal::new(self.addr.idx, self.addr.ty),
4320            offset: self.offset + offset,
4321        }
4322    }
4323}
4324
4325impl<'a> Stack<'a> {
4326    fn slice(&self, range: Range<usize>) -> Stack<'a> {
4327        Stack {
4328            locals: &self.locals[range],
4329            opts: self.opts,
4330        }
4331    }
4332}
4333
4334struct VariantCase<'a> {
4335    src_i: u32,
4336    src_ty: Option<&'a InterfaceType>,
4337    dst_i: u32,
4338    dst_ty: Option<&'a InterfaceType>,
4339}
4340
4341fn variant_info<'a, I>(types: &ComponentTypesBuilder, cases: I) -> VariantInfo
4342where
4343    I: IntoIterator<Item = Option<&'a InterfaceType>>,
4344    I::IntoIter: ExactSizeIterator,
4345{
4346    VariantInfo::new(
4347        cases
4348            .into_iter()
4349            .map(|ty| ty.map(|ty| types.canonical_abi(ty))),
4350    )
4351    .0
4352}
4353
4354/// State for the iteration loop inside a sequence translation.
4355struct SequenceLoopState {
4356    remaining: TempLocal,
4357    cur_src_ptr: TempLocal,
4358    cur_dst_ptr: TempLocal,
4359}
4360
4361/// Holds all temporaries created by `begin_translate_sequence` so the
4362/// caller can emit a custom loop body before calling
4363/// `end_translate_sequence`.
4364struct SequenceTranslation<'a> {
4365    src_len: TempLocal,
4366    src_mem: Memory<'a>,
4367    dst_mem: Memory<'a>,
4368    src_opts: &'a Options,
4369    dst_opts: &'a Options,
4370    src_mem_opts: &'a LinearMemoryOptions,
4371    dst_mem_opts: &'a LinearMemoryOptions,
4372    loop_state: Option<SequenceLoopState>,
4373}
4374
4375enum MallocSize {
4376    Const(u32),
4377    Local(u32),
4378}
4379
4380struct WasmString<'a> {
4381    ptr: TempLocal,
4382    len: TempLocal,
4383    opts: &'a Options,
4384}
4385
4386struct TempLocal {
4387    idx: u32,
4388    ty: ValType,
4389    needs_free: bool,
4390}
4391
4392impl TempLocal {
4393    fn new(idx: u32, ty: ValType) -> TempLocal {
4394        TempLocal {
4395            idx,
4396            ty,
4397            needs_free: false,
4398        }
4399    }
4400}
4401
4402impl std::ops::Drop for TempLocal {
4403    fn drop(&mut self) {
4404        if self.needs_free {
4405            panic!("temporary local not free'd");
4406        }
4407    }
4408}
4409
4410impl From<FlatType> for ValType {
4411    fn from(ty: FlatType) -> ValType {
4412        match ty {
4413            FlatType::I32 => ValType::I32,
4414            FlatType::I64 => ValType::I64,
4415            FlatType::F32 => ValType::F32,
4416            FlatType::F64 => ValType::F64,
4417        }
4418    }
4419}