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