Skip to main content

wasmtime_environ/fact/
trampoline.rs

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