Skip to main content

wasmtime_environ/fact/
trampoline.rs

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