Skip to main content

wasmtime_environ/fact/
trampoline.rs

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