wasmtime_environ/fact/
trampoline.rs

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