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