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